シンクロナむザヌ参照java.util.concurrent。*

この出版物の目的は、java.util.concurrentパッケヌゞのシンクロナむザヌを完党に分析するこずではありたせん。 たず第䞀に、トピックぞの゚ントリを容易にし、スレッドを同期するためのクラスの実甚的なアプリケヌションの可胜性を瀺す参考曞ずしおそれを曞いおいたす以降、スレッド=スレッド。



java.util.concurrentには、機胜に応じおグルヌプに分類できる倚くの異なるクラスがありたす。ConcurrentCollections、Executors、Atomicsなどです。 これらのグルヌプの1぀はシンクロナむザヌです。







シンクロナむザヌは、フロヌを同期するための補助ナヌティリティです。これにより、開発者は、フロヌの動䜜を調敎および/たたは制限し、基本的な蚀語プリミティブモニタヌよりも高い抜象床を提䟛できたす。





セマフォ



セマフォシンクロナむザは、 セマフォ同期パタヌンを実装したす。 共有リ゜ヌスぞのアクセスを制限する必芁がある堎合、ほずんどの堎合、セマフォが必芁です。 このクラスのコンストラクタ Semaphore(int permits)



たたはSemaphore(int permits, boolean fair)



は、セマフォが指定されたリ゜ヌスを同時に䜿甚できるスレッドの数を枡す必芁がありたす。







アクセスはカりンタによっお制埡されたす。最初は、カりンタ倀はint permits



です。ストリヌムが特定のコヌドブロックに入るず、カりンタ倀は1枛少し、ストリヌムがそれを離れるず増加したす。 カりンタヌ倀がれロの堎合、誰かがブロックを離れるたで珟圚のフロヌがブロックされたす permits = 1



生掻の䟋ずしお、蚺療所のオフィスにキュヌを持ち蟌むこずができたす患者がオフィスを離れるず、ランプが点滅し、次の患者が入宀したす 



セマフォの公匏ドキュメント。



セマフォの䟋
次の䟋を考えおみたしょう。 䞀床に5台たでしか駐車できない駐車堎がありたす。 駐車堎が完党に満車の堎合、新しく到着した車は、少なくずも1垭が空くたで埅たなければなりたせん。 その埌、圌は駐車するこずができたす。



 import java.util.concurrent.Semaphore; public class Parking { //   - true,  - false private static final boolean[] PARKING_PLACES = new boolean[5]; //  "",     //aquire()       private static final Semaphore SEMAPHORE = new Semaphore(5, true); public static void main(String[] args) throws InterruptedException { for (int i = 1; i <= 7; i++) { new Thread(new Car(i)).start(); Thread.sleep(400); } } public static class Car implements Runnable { private int carNumber; public Car(int carNumber) { this.carNumber = carNumber; } @Override public void run() { System.out.printf(" №%d   .\n", carNumber); try { //acquire()          , //   ,        , //     SEMAPHORE.acquire(); int parkingNumber = -1; //     synchronized (PARKING_PLACES){ for (int i = 0; i < 5; i++) if (!PARKING_PLACES[i]) { //   PARKING_PLACES[i] = true; //  parkingNumber = i; //  ,   System.out.printf(" №%d    %d.\n", carNumber, i); break; } } Thread.sleep(5000); //  ,   synchronized (PARKING_PLACES) { PARKING_PLACES[parkingNumber] = false;//  } //release(), ,   SEMAPHORE.release(); System.out.printf(" №%d  .\n", carNumber); } catch (InterruptedException e) { } } } }
      
      





プログラムの結果
1号車は駐車堎たで運転したした。

1号車は0に駐車されたした。

2号車は駐車堎たで運転したした。

2号車は1番に駐車されたした。

3号車は駐車堎たで運転したした。

3号車は2番に駐車されたした。

4号車は駐車堎たで運転したした。

4号車は3番に駐車されたした。

5号車は駐車堎たで運転したした。

5号車は4番に駐車されたした。

6号車は駐車堎たで運転したした。

7号車は駐車堎たで運転したした。

1号車が駐車堎を出たした。

車番号6は0に駐車されたした。

2号車が駐車堎を出たした。

7番の車が所定の堎所に駐車しおいたす。

3号車が駐車堎を出たした。

4号車が駐車堎を出たした。

5号車が駐車堎を出たした。

6号車が駐車堎を出たした。

7号車が駐車堎を出たした。



セマフォはこの問題を解決するのに最適です駐車スペヌスがない堎合カりンタヌは0、車ストリヌムが駐車特定のコヌドブロックに移動し、共有リ゜ヌスを䜿甚するこずを蚱可したせん。セマフォクラスは耇数のキャプチャず解攟をサポヌトするこずに泚意しおください䞀床に蚱可されたすが、このタスクは必芁ありたせん。





CountDownLatch



CountDownLatchカりントダりン付きロックを䜿甚するず、コヌドブロック内の任意の数のスレッドが、他のスレッドで特定の数の操䜜が完了するたで埅機しおから、アクティビティを継続するこずができたす。 コンストラクタヌCountDownLatch CountDownLatch(int count)



は、ロックがブロックされたスレッドを「解攟」するために実行する必芁がある操䜜の数を枡す必芁がありたす。







ブロッキングフロヌは、カりンタヌを䜿甚しお削陀されたす。アクティブなストリヌムは、特定の操䜜を実行するず、カりンタヌの倀を枛らしたす。 カりンタヌが0に達するず、埅機䞭のすべおのスレッドがロック解陀され、実行が継続されたす人生のCountDownLatchの䟋は、゚クスカヌショングルヌプのコレクションです。特定の人数が集たるたで、゚クスカヌションは開始されたせん。



CountDownLatchの公匏ドキュメント。



CountDownLatchの䟋
次の䟋を考えおみたしょう。 カヌレヌスをしたいです。 5台の車がレヌスに参加したす。 レヌスを開始するには、次の条件が満たされおいる必芁がありたす。

  1. 5台の車はそれぞれ、スタヌトラむンたで走りたした。
  2. コマンド「To start」が䞎えられたした。
  3. コマンド「譊告」が䞎えられたした。
  4. コマンド「March」が䞎えられたした。
すべおの車が同時にスタヌトするこずが重芁です。

 import java.util.concurrent.CountDownLatch; public class Race { // CountDownLatch  8 "" private static final CountDownLatch START = new CountDownLatch(8); //    private static final int trackLength = 500000; public static void main(String[] args) throws InterruptedException { for (int i = 1; i <= 5; i++) { new Thread(new Car(i, (int) (Math.random() * 100 + 50))).start(); Thread.sleep(1000); } while (START.getCount() > 3) //,     Thread.sleep(100); //  .  ,  100ms Thread.sleep(1000); System.out.println(" !"); START.countDown();// ,    1 Thread.sleep(1000); System.out.println("!"); START.countDown();// ,    1 Thread.sleep(1000); System.out.println("!"); START.countDown();// ,    1 //   ,     //  } public static class Car implements Runnable { private int carNumber; private int carSpeed;//,     public Car(int carNumber, int carSpeed) { this.carNumber = carNumber; this.carSpeed = carSpeed; } @Override public void run() { try { System.out.printf(" №%d    .\n", carNumber); //     -   //   1 START.countDown(); // await()  ,  ,   ,  // CountDownLatch    0 START.await(); Thread.sleep(trackLength / carSpeed);//    System.out.printf(" №%d !\n", carNumber); } catch (InterruptedException e) { } } } }
      
      





プログラムの結果
1号車がスタヌトラむンたで走りたした。

2号車はスタヌトラむンたで走りたした。

3号車はスタヌトラむンたで走りたした。

4号車はスタヌトラむンたで走りたした。

5号車はスタヌトラむンたで走りたした。

はじめに

泚意

3月

4号車が完成したした

1号車が終了したした

3号車が完成したした

5号車が完成したした

2号車が完成したした



CountDownLatchはさたざたな同期スキヌムで䜿甚できたす。たずえば、1぀のスレッドが䜜業を行っおいる間、他のスレッドを埅機させたり、逆に他のスレッドが䜜業を行うのを埅機させたりできたす。





CyclicBarrier



CyclicBarrierは、 バリア同期パタヌンを実装したす。 ルヌプバックバリアは、指定された数の䞊列スレッドが出䌚っおブロックする同期ポむントです。 すべおの利益が流れるずすぐに、オプションのアクションが実行されたたはバリアがそれなしで初期化された堎合は実行されたせん、完了埌、バリアが解陀され、埅機䞭のフロヌが「解攟」されたす。 バリアコンストラクタヌ CyclicBarrier(int parties)



およびCyclicBarrier(int parties, Runnable barrierAction)



は、「䌚う」べきパヌティヌの数ず、オプションで、パヌティヌが䌚うずき、ただしパヌティヌが䌚う前に発生するアクションを枡す必芁がありたす。 「リリヌスされたした。」







バリアはCountDownLatchに䌌おいたすが、2぀の䞻な違いは、カりンタがれロになった埌は「ロック」を再利甚できず、バリアが壊れた埌でも再び䜿甚できるこずです。 CyclicBarrierは、スレッドが実行された埌にのみスレッドを「収集」 join()



メ゜ッドの代替です。



CyclicBarrierの公匏ドキュメント。



CyclicBarrierのケヌススタディ
次の䟋を考えおみたしょう。 フェリヌサヌビスがありたす。 フェリヌは䞀床に3台の車を茞送できたす。 フェリヌを再床運転しないようにするには、少なくずも3台の車が亀差点に集たるずきにフェリヌを送る必芁がありたす。



 import java.util.concurrent.CyclicBarrier; public class Ferry { private static final CyclicBarrier BARRIER = new CyclicBarrier(3, new FerryBoat()); //      ,   ,  //    .  ,   . public static void main(String[] args) throws InterruptedException { for (int i = 0; i < 9; i++) { new Thread(new Car(i)).start(); Thread.sleep(400); } } //,        public static class FerryBoat implements Runnable { @Override public void run() { try { Thread.sleep(500); System.out.println("  !"); } catch (InterruptedException e) { } } } //,     public static class Car implements Runnable { private int carNumber; public Car(int carNumber) { this.carNumber = carNumber; } @Override public void run() { try { System.out.printf(" №%d    .\n", carNumber); //        ,    await() //    ,        BARRIER.await(); System.out.printf(" №%d  .\n", carNumber); } catch (Exception e) { } } } }
      
      





プログラムの結果
車番号0はフェリヌたで運転したした。

1号車がフェリヌたで運転したした。

2号車がフェリヌたで走りたした。

3号車がフェリヌたで走りたした。

フェリヌフェリヌカヌ

2号車は動き続けたした。

1号車は動き続けたした。

車番号0は動き続けたした。

4号車がフェリヌたで走りたした。

5号車がフェリヌたで走りたした。

6号車がフェリヌたで運転したした。

フェリヌフェリヌカヌ

5号車は動き続けたした。

4号車は動き続けたした。

3号車は動き続けたした。

7号車がフェリヌたで走りたした。

8号車がフェリヌたで走りたした。

フェリヌフェリヌカヌ

8号車は動き続けたした。

6号車は動き続けたした。

7号車は動き続けたした。



3぀のストリヌムがawait()



メ゜ッドに到達するず、バリアアクションがトリガヌされ、蓄積されたフェリヌからの3台の車がフェリヌされたす。 この埌、新しいサむクルが始たりたす。





゚クスチェンゞャヌ<V>



䞡方のストリヌムの操䜜のある時点で、2぀のストリヌム間でデヌタを亀換するには、亀換機亀換機が必芁になる堎合がありたす。 ゚クスチェンゞャヌは䞀般化されたクラスであり、転送するオブゞェクトのタむプによっおパラメヌタヌ化されたす。







゚クスチェンゞャヌは、スレッドのペアの同期ポむントです。 exchange()



メ゜ッドを呌び出すスレッドexchange()



ブロックされ、別のスレッドを埅機したす。 別のスレッドが同じメ゜ッドを呌び出すず、オブゞェクトが亀換されたす。各オブゞェクトは、 exchange()



メ゜ッドで他のオブゞェクトぞの匕数を受け取りたす。 亀換機がnull



倀の転送をサポヌトしおいるこずは泚目に倀したす。 これにより、オブゞェクトを䞀方向に、たたは単玔に2぀のストリヌムの同期ポむントずしお転送するこずができたす。



Exchangerの公匏ドキュメント。



゚クスチェンゞャヌの䟋
次の䟋を考えおみたしょう。 2぀のトラックがありたす1぀はポむントAからポむントDに、もう1぀はポむントBからポむントCに行きたす。道路ADずBCはポむントEで亀差したす。ポむントAずBから、荷物はポむントCずDに配達されたす。このため、ポむントEのトラック関連するパッケヌゞを満たし、亀換する必芁がありたす。



 import java.util.concurrent.Exchanger; public class Delivery { // ,     String private static final Exchanger<String> EXCHANGER = new Exchanger<>(); public static void main(String[] args) throws InterruptedException { String[] p1 = new String[]{"{ A->D}", "{ A->C}"};//   1-  String[] p2 = new String[]{"{ B->C}", "{ B->D}"};//   2-  new Thread(new Truck(1, "A", "D", p1)).start();// 1-     D Thread.sleep(100); new Thread(new Truck(2, "B", "C", p2)).start();// 2-      } public static class Truck implements Runnable { private int number; private String dep; private String dest; private String[] parcels; public Truck(int number, String departure, String destination, String[] parcels) { this.number = number; this.dep = departure; this.dest = destination; this.parcels = parcels; } @Override public void run() { try { System.out.printf("  №%d : %s  %s.\n", number, parcels[0], parcels[1]); System.out.printf(" №%d    %s   %s.\n", number, dep, dest); Thread.sleep(1000 + (long) Math.random() * 5000); System.out.printf(" №%d    .\n", number); parcels[1] = EXCHANGER.exchange(parcels[1]);//  exchange()     //    exchange(),      System.out.printf("  №%d     %s.\n", number, dest); Thread.sleep(1000 + (long) Math.random() * 5000); System.out.printf(" №%d   %s  : %s  %s.\n", number, dest, parcels[0], parcels[1]); } catch (InterruptedException e) { } } } }
      
      





プログラムの結果
以䞋がトラック1番に積茉されたした{パッケヌゞA-> D}および{パッケヌゞA-> C}。

トラックNo. 1は、ポむントAからポむントDたで運転したした。

トラックNo. 2に搭茉{パッケヌゞB-> C}および{パッケヌゞB-> D}。

トラック2は、ポむントBからポむントCたで運転したした。

トラック1番がポむントEに到着したした。

トラックNo. 2はポむントEに到着したした。

アむテムCのトラックはトラック2に移動したした。

アむテムDのトラックはトラック1に移動したした。

トラックNo. 2がCに到着し、配達されたした{パッケヌゞB-> C}および{パッケヌゞA-> C}。

トラックNo. 1がDに到着し、配達されたした{パッケヌゞA-> D}および{パッケヌゞB-> D}。



ご芧のずおり、1台のトラック1぀のストリヌムがポむントE同期ポむントに到達に到着するず、別のトラック別のストリヌムがポむントE同期ポむントに到達に到達するたで埅機したす。 この埌、小包亀換が行われストリング、䞡方のトラックストリヌムがパス䜜業を継続したす。





フェむザヌ



CyclicBarrierのようなPhaserphaserは、 Barrier同期パタヌンの実装ですが、CyclicBarrierずは異なり、より柔軟性がありたす。 このクラスを䜿甚するず、共通アクションの単䞀のフェヌズたたはステヌゞを衚すスレッドを同期できたす。 CyclicBarrierず同様に、Phaserはメンバヌストリヌムが出䌚う同期ポむントです。 すべおの関係者が到着するず、Phaserは次のフェヌズに進み、再び完了するのを埅ちたす。



PhaserずCyclicBarrierを比范するず、Phaserの次の重芁な機胜を匷調できたす。



Phaserオブゞェクトは、コンストラクタヌのいずれかを䜿甚しお䜜成されたす。



 Phaser() Phaser(int parties)
      
      





Partyパラメヌタヌは、アクションのフェヌズを実行するパヌティヌの数を瀺したす。 最初のコンストラクタヌは、偎面のないPhaserオブゞェクトを䜜成し、この堎合のバリアも「閉じられたす」。 2番目のコンストラクタヌは、コンストラクタヌに枡されるサむドの数を登録したす。 バリアは、すべおの関係者が到着したずき、たたは最埌の参加者が削陀されたずきに開きたす。 Phaserクラスには、ただ芪Phaserオブゞェクトが枡されるコンストラクタがありたすが、それらは考慮したせん。



䞻な方法



Phaserの公匏ドキュメント。

フェむザヌの䟋
次の䟋を考えおみたしょう。 5぀の停留所がありたす。 最初の4人は乗客を立たせおバスを埅぀こずができたす。 バスは公園を出お、各停留所でしばらく止たりたす。 最終停車埌、バスは公園に行きたす。 乗客を迎え、適切な停留所で降ろす必芁がありたす。

 import java.util.ArrayList; import java.util.concurrent.Phaser; public class Bus { private static final Phaser PHASER = new Phaser(1);//    // 0  6 -   , 1 - 5  public static void main(String[] args) throws InterruptedException { ArrayList<Passenger> passengers = new ArrayList<>(); for (int i = 1; i < 5; i++) { //    if ((int) (Math.random() * 2) > 0) passengers.add(new Passenger(i, i + 1));//     if ((int) (Math.random() * 2) > 0) passengers.add(new Passenger(i, 5)); //     } for (int i = 0; i < 7; i++) { switch (i) { case 0: System.out.println("   ."); PHASER.arrive();//  0  1  -  break; case 6: System.out.println("   ."); PHASER.arriveAndDeregister();//  ,   break; default: int currentBusStop = PHASER.getPhase(); System.out.println(" № " + currentBusStop); for (Passenger p : passengers) //,      if (p.departure == currentBusStop) { PHASER.register();// ,      p.start(); //   } PHASER.arriveAndAwaitAdvance();//    } } } public static class Passenger extends Thread { private int departure; private int destination; public Passenger(int departure, int destination) { this.departure = departure; this.destination = destination; System.out.println(this + "    № " + this.departure); } @Override public void run() { try { System.out.println(this + "   ."); while (PHASER.getPhase() < destination) //      () PHASER.arriveAndAwaitAdvance(); //        Thread.sleep(1); System.out.println(this + "  ."); PHASER.arriveAndDeregister(); //     } catch (InterruptedException e) { } } @Override public String toString() { return "{" + departure + " -> " + destination + '}'; } } }
      
      





プログラムの結果
乗客{1-> 2}はストップ番号1で埅っおいたす

乗客{1-> 5}は、ストップ番号1で埅っおいたす

乗客{2-> 3}はストップ番号2で埅っおいたす

乗客{2-> 5}はストップ番号2で埅っおいたす

乗客{3-> 4}はストップ番号3で埅っおいたす

乗客{4-> 5}はストップ番号4で埅っおいたす

乗客{4-> 5}はストップ番号4で埅っおいたす

バスは公園を出発したした。

ストップナンバヌ1

乗客{1-> 5}がバスに乗り蟌んだ。

乗客{1-> 2}がバスに乗り蟌みたした。

ストップナンバヌ2

乗客{2-> 3}がバスに乗り蟌んだ。

{1 -> 2} .

{2 -> 5} .

№ 3

{2 -> 3} .

{3 -> 4} .

№ 4

{4 -> 5} .

{3 -> 4} .

{4 -> 5} .

№ 5

{1 -> 5} .

{2 -> 5} .

{4 -> 5} .

{4 -> 5} .

.





ずころで、フェむザヌ機胜はCountDownLatchの動䜜を再珟できたす。

Phaserを䜿甚したCountDownLatchの䟋
 import java.util.concurrent.Phaser; public class NewRace { private static final Phaser START = new Phaser(8); private static final int trackLength = 500000; public static void main(String[] args) throws InterruptedException { for (int i = 1; i <= 5; i++) { new Thread(new Car(i, (int) (Math.random() * 100 + 50))).start(); Thread.sleep(100); } while (START.getRegisteredParties() > 3) //,     Thread.sleep(100); //  .  ,  100ms Thread.sleep(100); System.out.println(" !"); START.arriveAndDeregister(); Thread.sleep(100); System.out.println("!"); START.arriveAndDeregister(); Thread.sleep(100); System.out.println("!"); START.arriveAndDeregister(); } public static class Car implements Runnable { private int carNumber; private int carSpeed; public Car(int carNumber, int carSpeed) { this.carNumber = carNumber; this.carSpeed = carSpeed; } @Override public void run() { try { System.out.printf(" №%d    .\n", carNumber); START.arriveAndDeregister(); START.awaitAdvance(0); Thread.sleep(trackLength / carSpeed); System.out.printf(" №%d !\n", carNumber); } catch (InterruptedException e) { } } } }
      
      












誰かが重宝しおくれたら、ずおも嬉しいです=



Phaserの詳现はこちら。

シンクロナむザヌの詳现ず䟋を参照するには、こちらをご芧ください。



java.util.concurrentの優れた抂芁はこちらをご芧ください。



All Articles