Статьи

Пять продвинутых синхронизаторов Java, которых вы, вероятно, не знаете

Помимо общей синхронизации, которая основана на бите блокировки, который есть у каждого объекта Java, у вас есть более сложные синхронизаторы в Java, такие как:

  • Семафор — используйте концепцию разрешения, чтобы указать максимальное количество разрешенных потоков в одном месте. Когда вы используете значение 1, поведение аналогично синхронизации, также называется двоичным семафором. Однако здесь есть большая разница: вы получаете разрешение на семафор, а не блокирующий объект, это просто переменная для подсчета, когда поток получает разрешение и когда поток выпускает разрешение, своего рода счетчик. Единственное, что у вас есть — блокировка потоков, пока не будет доступно разрешение. В приведенном ниже примере мы определяем 3 как количество разрешений, поэтому после получения 3 поток 4 будет ожидать освобождения, прежде чем продолжить его выполнение.
01
02
03
04
05
06
07
08
09
10
// Define the semaphore to control 3 permits.
// 3 Threads can acquire the mySemaphore
Semaphore mySemaphore = new Semaphore(3, true);
 
// 3 threads can execute this line of code. The 4 thread must wait for a release
mySemaphore.acquire();
 
// .. somewhere in the code a thread releases the mySemaphore,
// and now the next waiting thread can acquire
mySemaphore.release();
    • CountDownLatch — Инициализируйте этот класс числом (для обратного отсчета), и когда он достигнет 0, ожидающий поток разблокируется и продолжит свой путь. (После ожидания защелка не может быть использована повторно)
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
// Initializes a countdown starting from 3
CountDownLatch latch = new CountDownLatch(3);
 
// ... other threads are running...
 
// Some thread blocks and waits for the latch countdown
// to reach "0"
latch.await();
 
// ... code, methods, other objects... etc...
 
// ... at some place the OTHER threads do the countdown,
// decrementing the latch.. when it reachs 0
// the blocked thread with the "await()" follows its way
latch.countDown();
  • CyclicBarrier — этот класс ведет себя как обратный к CountDownLatch. После N await () заблокированные потоки могут следовать своим путем. (CyclicBarrier можно использовать повторно)
1
2
3
4
5
6
7
8
// 3 threads must await before can unblock
CyclicBarrier barrier = new CyclicBarrier(3);
 
// threads they block here until the 3 is reached
barrier.await();
 
// after 3 threads in await this code will run!
System.out.println("Thank you to the 3 friends who awaited for me!”);
  • Phaser — очень сложный синхронизатор, сочетание CountDownLatch и CyclicBarrier, с множеством настраиваемых параметров. Если вам нужно поведение, подобное двум предыдущим синхронизаторам, но их было недостаточно, вам нужно углубиться в этот. Он ведет себя как CyclicBarrier, но вы можете зарегистрировать набор потоков и в любое время отменить регистрацию, достигнув уровня настройки, невозможного с другими синхронизаторами. Подумайте о необходимости ожидания потоков, прежде чем вы сможете продолжить или запустить другой набор задач. Больше информации об этом на сайте Oracle:

http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/Phaser.html

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
void runTasks(List<Runnable> tasks) {
   // Initialize the phaser, "1" to register self
   final Phaser phaser = new Phaser(1);
   // create and start threads
   for (final Runnable task : tasks) {
 
     // register here
     phaser.register();
     new Thread() {
       public void run() {
 
         // await all creation
         phaser.arriveAndAwaitAdvance();
         task.run();
       }
     }.start();
   }
 
   // allow threads to start and deregister self
   phaser.arriveAndDeregister();
 }
  • Обменник . Лучшее объяснение дает сам документ Oracle: «Точка синхронизации, в которой потоки могут связывать и менять элементы в парах ». Один поток хочет отправить информацию другому потоку и блокирует ожидание отправки данных, и в EXCHANGE получает то, что другой поток тоже хочет отправить! Такое поведение происходит с обеих сторон!
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
// Create the exchanger.
// We choose String as the data datatype
Exchanger<String> ex = new Exchanger<String>();
 
//
// .... Somewhere at Thread 1,
//
 
// I will block until I can send str1 to Thread 2, and receive a value too!
String str1 = "value to send from Thread 1 to Thread 2";
String valueReturnedFromThread2 = ex.exchange(str1);
 
//
// ... Somewhere at Thread 2,
//
 
// I will block until I can send str2 to Thread 1
// I will receive a value from Thread 1 too!
String str2 = "value to send to Thread 1 from Thread 2";
String valueReturnedFromThread1 = ex.exchange(str2);