Specific synchronization primitives #
In Java there are some possibilities for synchronization. The monitor lock with the
synchronized keyword is only one of them.
- Lock & Condition
- Read-Write Lock
The semaphore #
The semaphore can be used in very many places, since other synchronization primitives are built on top of it.
The semaphore is initialized with either 0, 1 or any other arbitrary number. As long as the number is greater than 0,
aquire(N) can be used to decrease the number by N. If the number is 0, any thread that wants to take something out waits until someone has increased the number by N with
release(N). The user can think of the semaphore as a pot of entries. Threads are allowed to continue if they have a ticket, and if they don’t, they wait until there is one in the pot.
The semaphore can be used for many things. If it always oscillates between 1 or 0, a resource like a variable or a service can be protected. For semaphores that oscillate between N and 0, N threads can use the resource at the same time (e.g. for quotas).
Lock & Condition #
If a resource is locked and waits for a certain condition, at best the
ReentrantLock class simplifies the code. For example, a condition on “nonFull” and on “nonEmpty” can be created. This can bring a substantial performance profit, because it can be waited now on “nonFull” or “nonEmpty” and it is not woken up unnecessarily many Threads.
private Lock monitor = new ReentrantLock(true); private Condition nonFull = monitor.newCondition(); private Condition nonEmpty = monitor.newCondition();
Allowing only one thread to read or write a resource each time may be unnecessary. Normally, multiple threads should be allowed to read, but only one thread should be allowed to write. For this purpose there is the class
ReadWriteLock rwLock = new ReentrantReadWriteLock(true); rwLock.readLock().lock(); // read-only accesses rwLock.readLock().unlock(); rwLock.writeLock().lock(); // write (and read) accesses rwLock.writeLock().unlock();
The CountDownLatch blocks as long as the counter is > 0. It is used to synchronize multiple threads in time. For example, it can be used to make all threads wait until all threads have reached the barrier.
CountDownLatch ready = new CountDownLatch(N); \\ `` in the threads then respectively ready.countDown(); ready.await();
countDown() always reduces the counter by 1, the CountDownLatch can only be used once.
The CyclicBarrier works similar to the CountDownLatch, but can be used multiple times. It closes after all threads have passed.
CyclicBarrier start = new CyclicBarrier(N); start.await();
Exchanger an object can be exchanged. The method
V exchange(V x) blocks until the other thread calls the same method. The return value is the object of the other thread.