最近、ビジネスロジックの正確性を確保するためにロックを必要とするいくつかの高い並行性シナリオに遭遇しました。ロック後のパフォーマンスはあまり影響を受けません。最初のアイデアは、タイムスタンプやIDなどのキーワードを介してデータをロックして、さまざまな種類のデータ処理の並行性を保証することです。 Java独自のAPIによって提供されるロックの粒度は大きすぎて、これらのニーズを同時に満たすことは困難です。
1。セグメントロック
Concurrenthashmapのセグメンテーションのアイデアに基づいて、特定の数のロックが作成され、それを使用すると、対応するロックがキーに従って返されます。これは、いくつかの実装の中で最もシンプルで最高のパフォーマンスであり、最終的に採用されたロック戦略です。コードは次のとおりです。
/***セグメントロック、システムは一定数の元のロックを提供し、着信オブジェクトのハッシュ値に基づいて対応するロックを取得し、ロックを追加します*注:ロックされたオブジェクトのハッシュ値が変更された場合、ロックを正常にリリースできない場合があります!!! */public class segmentlock <t> {private integer segments = 16; //デフォルト番号プライベートファイナルハッシュマップ<integer、reentrantlock> lockmap = new Hashmap <>(); public segmentLock(){init(null、false); } public segmentlock(integer counts、boolean fair){init(counts、fair); } private void init(integer counts、boolean fair){if(counts!= null){segments = counts; } for(int i = 0; i <segments; i ++){lockmap.put(i、new Reentrantlock(fair)); }} public void lock(t key){reentrantlock lock = lockmap.get((key.hashcode()>>> 1)%セグメント); lock.lock(); } public void Unlock(t key){reentrantlock lock = lockmap.get((key.hashcode()>>> 1)%セグメント); lock.unlock(); }}2。ハッシュロック
上記のセグメント化されたロックに基づいて開発された2番目のロック戦略は、真の細粒ロックを実現することです。異なるハッシュ値を持つ各オブジェクトは、独自の独立したロックを取得できます。テストでは、ロックされたコードが迅速に実行されると、効率はセグメントロックよりも約30%遅くなります。長い時間のかかる操作がある場合、パフォーマンスの感覚はより良いはずです。コードは次のとおりです。
パブリッククラスのハッシュロック<t> {private boolean isfair = false;プライベートファイナルセグメントロック<t> segmentlock = new segmentlock <>(); // segment lock private final concurrenthashmap <t、lockinfo> lockmap = new concurrenthashmap <>(); public hashlock(){} public hashlock(boolean fair){isfair = fair; } public void lock(t key){lockinfo lockinfo; segmentlock.lock(key); try {lockinfo = lockmap.get(key); if(lockinfo == null){lockinfo = new lockinfo(isfair); lockmap.put(key、lockinfo); } else {lockinfo.count.incrementAndget(); }}最後に{segmentlock.unlock(key); } lockinfo.lock.lock(); } public void Unlock(t key){lockinfo lockinfo = lockmap.get(key); if(lockinfo.count.get()== 1){segmentlock.lock(key); try {if(lockinfo.count.get()== 1){lockmap.remove(key); }}最後に{segmentlock.unlock(key); }} lockinfo.count.decrementAndget(); lockinfo.unlock(); } private static class lockinfo {public reintrantlock lock; public AtomicInteger count = new AtomicInteger(1); private lockinfo(boolean fair){this.lock = new ReentrantLock(FAIR); } public void lock(){this.lock.lock(); } public void lock(){this.lock.unlock(); }}}3.弱い参照ロック
セグメント化されたロックが導入されてロックの作成と破壊の同期を確保するため、ハッシュロックには常に欠陥があります。そのため、パフォーマンスと細かいロックを模索するために3番目のロックが書き込まれました。このロックのアイデアは、Javaの弱い参照の助けを借りてロックを作成し、追加の消費を避けるためにJVMのゴミコレクションに対するロックの破壊を引き渡すことです。
並行ハッシュマップがロック容器として使用されているため、セグメント化されたロックを本当に取り除くことができないことは少し残念です。このロックのパフォーマンスは、ハッシュロックのパフォーマンスよりも約10%高速です。ロックコード:
/***独立したハッシュごとに独立したロック機能を提供する弱い参照ロックプライベートリファレンスキュー<REENTRANTLOCK> queue = new ReferenceQueue <>(); public ReentrantLock get(t key){if(lockmap.size()> 1000){clearEmptyRef(); } weakreference <ReentrantLock> lockref = lockmap.get(key); REENTRANTLOCK LOCK =(LOCKREF == null?null:lockref.get()); while(lock == null){lockmap.putifabsent(key、new weaklockref <>(new ReentrantLock()、Queue、key)); lockref = lockmap.get(key); lock =(lockref == null?null:lockref.get()); if(lock!= null){return lock; } clearEmptyRef(); }ロックを返します。 } @suppresswarnings( "unchecked")private void clearEmptyRef(){参照<? ReentrantLock> refを拡張します。 while((ref = queue.poll())!= null){weaklockref <t、? reintrantlock> weaklockref =(weaklockref <t、?redentrantlock>)ref; lockmap.remove(wiedlockref.key); }} private static final class weaklockref <t、k> extends weakreference <k> {final t key; private weaklockref(k reference、referencequeue <?super k> q、t key){super(referent、q); this.key = key; }}}ポストスクリプト
最初は、LocksupportとAQSを使用して細粒ロックを実装したかったのです。私が書いたように、実装されているものはJavaネイティブロックのものとそれほど変わらないことがわかったので、Java独自のロックのカプセル化をあきらめました。
実際、これらのきめの細かいロックを実装した後、セグメンテーションのアイデアを介して特別なスレッドにデータを送信するなど、新しいアイデアがあります。