2. In this chapter, we will cover:
● Synchronizing a method
● Arranging independent attributes in
synchronized classes
● Using conditions in synchronized code
● Synchronizing a block of code with a Lock
● Synchronizing data access with read/write
locks
● Modifying Lock fairness
● Using multiple conditions in a Lock
3. Introduction
● Critical section
A critical section is a block of code that
accesses a shared resource and can't be
executed by more than one thread at the same
time.
4. Introduction
● Synchronization mechanisms
When a thread wants access to a critical section, it uses
one of those synchronization mechanisms to find out if
there is any other thread executing the critical section.
If not, the thread enters the critical section.
Otherwise, the thread is suspended by the
synchronization mechanism until the thread that is
executing the critical section ends it.
When more than one thread is waiting for a thread to
finish the execution of a critical section, the JVM chooses
one of them, and the rest wait for their turn.
6. Synchronizing a method
Every method declared with the
synchronized keyword is a critical section
and Java only allows the execution of one of
the critical sections of an object.
Static methods have a different behavior. Two
threads can access two different
synchronized methods if one is static and
the other one is not.
7. We will have a bank account and two threads;
one that transfers money to the account and
another one that withdraws money from the
account.
Synchronization mechanisms ensures that
the final balance of the account will be correct.
8. How to do it…
Create a class called: Account
One attribute: private double amount;
Two method:
addAmount() substractAmount();
12. The order of execution of the threads is not
guaranteed by the JVM.
Using the synchronized keyword, we
guarantee correct access to shared data in
concurrent applications.
13. There is more...
The synchronized keyword penalizes the
performance of the application, so you must
only use it on methods that modify shared data
in a concurrent environment.
You can use recursive calls with
synchronized methods.
14. We can use the synchronized keyword to
protect the access to a block of code instead of
an entire method.
15. Arranging independent attributes in
synchronized classes
When executing the synchronized method
which you use the this keyword as a
parameter, you can only use other object
references.
But, now...
● Two independent attributes, synchronize the
access to each variable.
● One thread accessing one attribute, another
thread accessing another one.
16. Simulates a cinema with two screens and two
ticket offices.
When a ticket office sells tickets, they are for
one of the two cinemas, but not for both.
So the numbers of free seats in each cinema
are independent attributes.
21. How it works...
When you use an object as a parameter of
synchronized method, JVM guarantees that:
Only one thread can have access to all the
blocks of code protected with that object.
Note: objects, not classes.
22. We have two objects that controls access to the
vacanciesCinema1 attribute and
vacanciesCinema2 attribute separately.
One thread can modify one attribute each time.
23. Using conditions in synchronized
code
Producer-consumer problem:
A classic problem in concurrent programming.
● A data buffer
○ A shared data structure
● One or more producers
○ Can’t save data in the buffer if it’s full
● One or more consumers
○ Can’t take data from the buffer if it’s empty
24. Java provides the wait(), notify() and
notifyAll() method s implemented in the
Object class.
25. wait() method
Called by a thread, inside a synchronized
block of code (Outside, JVM throws an
IllegalMonitorStateException
exception).
The JVM puts the thread to sleep and releases
the resources.
26. To wake up the thread, you must call the
notify() or notifyAll() method inside a
block of code protected by the same object.
35. Synchronizing a block of code with a
Lock
Another mechanism for the synchronization of
blocks of code.
Based on Lock interface and classes that
implement it (as ReentrantLock).
36. Compare Lock and synchronized
● The mechanism based on Lock interface is
more flexible.
● The Lock interfaces provide additional
functionalities, as tryLock().
● Allow a separation of read and write
operations having multiple readers and only
one modifier.
● Better performance
37. Using the Lock interface and the
ReentrantLock class that implements it,
implementing a program that simulates a print
queue.
43. The ReentrantLock class also allows the use
of recursive calls.
Avoid deadlocks.
Situation: Two or more threads are blocked
waiting for locks that never will be unlocked.
Reason: Both threads try to get the locks in the
opposite order.
44. Modifying Lock fairness
The constructor of the ReentrantLock and
ReentrantReadWriteLock classes admits a
boolean parameter named fair:
false: non-fair mode
selects one without any criteria.
true: fair mode
51. Synchronizing data access with
read/write locks
One of the most significant improvements
offered by locks is the ReadWriteLock
interface and the ReentrantReadWriteLock
class, the unique one that implements it.
Two locks: read/write
52. Use ReadWriteLock to control the access to
an object that stores the prices of two products.
59. Ensure the correct use of these locks, using
them with the same purposes.
When you get the read lock of a Lock
interface, you can’t modify the calue of the
variable.
60. Using multiple conditions in a Lock
A lock may be associated with one or more
conditions. These conditions are declared in
the Condition interface.
The Condition interface provides the
mechanisms to suspend a thread and to wake
up a suspended thread.
61. Condition
● Synchronized + monitor methods(wait,
notify and notifyAll)
● Lock + Condition
To obtain a Condition instance for a
particular Lock instance use its
newCondition() method.
62. Producer-consumer problem.
We have a data buffer, one or more producers
of data that save it in the buffer, and one or
more consumers of data that take it from the
buffer as explained earlier in this chapter
72. How it works...
When a thread calls the await() method of a
condition, it automatically frees the control of
the lock, so that another thread can get it and
begin the execution of the same, or another
critical section protected by that lock.
73. You must be careful with the use of await()
and signal().
You can use conditions with the ReadLock and
WriteLock locks of a read/write lock.