Learning Objectives
After completing this session, you will be able to:
Describe Thread Synchronization
Explain lifecycle of threads
Identify wait() method and notify() method of the Object class
Race Condition and How to Solve it
Race conditions occur when multiple, asynchronously executing threads access the
same object (called a shared resource) returning unexpected (wrong) results.
Example: Threads often need to share a common resource that is a file, with one
thread reading from the file while another thread writes to the file.
They can be avoided by synchronizing the threads, which access the shared resource.
An Unsynchronized Example
1 class TwoStrings {
2 static void print(String str1, String str2) {
3 System.out.print(str1);
4 try {
5 Thread.sleep(500);
6 } catch (InterruptedException ie) {
7 }
8 System.out.println(str2);
9 }
10 }
11
12 class PrintStringsThread implements Runnable {
13 Thread thread;
14 String str1, str2;
15 PrintStringsThread(String str1, String str2) {
16 this.str1 = str1;
17 this.str2 = str2;
18 thread = new Thread(this);
19 thread.start();
20 }
21 public void run() {
22 TwoStrings.print(str1, str2);
23 }
24 }
25
26 class TestThread {
27 public static void main(String args[]) {
28 new PrintStringsThread("Hello ", "there.");
29 new PrintStringsThread("How are ", "you?");
30 new PrintStringsThread("Thank you ",
31 "very much!");
32 }
33 }
Sample output:
Hello How are Thank you there.
you?
very much!
Synchronization: Locking an Object
A thread is synchronized by becoming an owner of the monitor of the object: Consider it as locking
an object
A thread becomes the owner of the monitor of the object in one of following ways:
Option 1: Use synchronized method
Option 2: Use synchronized statement on a common object
Option 1: Use Synchronized Method
1 class TwoStrings {
2 synchronized static void print(String str1,
3 String str2) {
4 System.out.print(str1);
5 try {
6 Thread.sleep(500);
7 } catch (InterruptedException ie) {
8 }
9 System.out.println(str2);
10 }
11 }
12
13 class PrintStringsThread implements Runnable {
14 Thread thread;
15 String str1, str2;
16 PrintStringsThread(String str1, String str2) {
17 this.str1 = str1;
18 this.str2 = str2;
19 thread = new Thread(this);
20 thread.start();
21 }
22 public void run() {
23 TwoStrings.print(str1, str2);
24 }
25 }
26
27 class TestThread {
28 public static void main(String args[]) {
29 new PrintStringsThread("Hello ", "there.");
30 new PrintStringsThread("How are ", "you?");
31 new PrintStringsThread("Thank you ",
32 "very much!");
33 }
34 }
Option 1: Executing Synchronized Method
Sample output:
Hello there.
How are you?
Thank you very much!
Option 2: Use Synchronized Statement on a Common Object
1 class TwoStrings {
2 static void print(String str1, String str2) {
3 System.out.print(str1);
4 try {
5 Thread.sleep(500);
6 } catch (InterruptedException ie) {
7 }
8 System.out.println(str2);
9 }
10 }
11
12 class PrintStringsThread implements Runnable {
13 Thread thread;
14 String str1, str2;
15 TwoStrings ts;
16 PrintStringsThread(String str1, String str2,
17 TwoStrings ts) {
18 this.str1 = str1;
19 this.str2 = str2;
20 this.ts = ts;
21 thread = new Thread(this);
22 thread.start();
23 }
24
25 public void run() {
26 synchronized (ts) {
27 ts.print(str1, str2);
28 }
29 }
30 }
31 class TestThread {
32 public static void main(String args[]) {
33 TwoStrings ts = new TwoStrings();
34 new PrintStringsThread("Hello ", "there.", ts);
35 new PrintStringsThread("How are ", "you?", ts);
36 new PrintStringsThread("Thank you ",
37 "very much!", ts);
38 }
39 }
wait() Method of Object Class
wait() method causes a thread to release the lock it is holding on an object allowing
another thread to run.
wait() method is defined in the Object class.
wait() can only be invoked from within synchronized code.
It should always be wrapped in a try block as it throws IOException.
wait() can only be invoked by the thread that owns the lock on the object.
When wait() is called, the thread becomes disabled for scheduling and lies dormant
until one of the four things occur:
Another thread invokes the notify() method for this object and the scheduler arbitrarily
chooses to run the thread.
Another thread invokes the notifyAll() method for this object.
Another thread interrupts this thread.
The specified wait() time elapses.
When one of the earlier occurs, the thread becomes re-available to the Thread
scheduler and competes for a lock on the object
Once it regains the lock on the object, everything resumes as if no suspension had
occurred.
notify() Method
notify() method wakes up a single thread thatis waiting on the monitor of this object:
If any threads are waiting on this object, then one of them is chosen to be awakened.
The choice is arbitrary and occurs at the discretion of the implementation.
notify() method can only be used within synchronized code.
The awakened thread will not be able to proceed until the current thread relinquishes
the lock on this object.
Try It Out:-
Problem Statement:
Write a program that illustrates the usage of synchronized statement in thread synchronization.
Code:
class ThreadSynchronizedStmtApp extends Thread {
static String message[] = { "Java", "is", "hot,", "aromatic,",
"and",
"invigorating." };
public static void main(String args[]) {
ThreadSynchronizedStmtApp thread1 = new
ThreadSynchronizedStmtApp("thread1: ");
ThreadSynchronizedStmtApp thread2 = new
ThreadSynchronizedStmtApp("thread2: ");
thread1.start();
thread2.start();
boolean thread1IsAlive = true;
boolean thread2IsAlive = true;
How It Works:
The synchronized statement is similar to a synchronized method in that. It is used to
acquire a lock on an object before performing an action.
The synchronized statement differs from a synchronized method in that. It can be used
with the lock of any object. The synchronized method can only be used with the lock of
object or class.
It also differs in a way that it applies toa statement block, rather than an entire
method.
The syntax of the synchronized statement is as follows:
synchronized (object) {
statement(s)
}
The statements enclosed by the braces are only executed when the current thread
acquires the lock for the object or class enclosed by parentheses.
Tips and Tricks:
Can you reuse a Thread object? Can you give it a newjob to do and then restart it by calling start()
again?
Solution:
No. Once the run() method of a thread has completed, the thread can never be
restarted. In fact, at that point the thread moves into the dead state. In the dead state,
the thread has finished its run() method and can never be restarted. The Thread object
might still be on the heap, as a living object that you can call other methods on (if
appropriate), but the Thread object has permanently lost its ‘threadness’. In other
words, the Thread object is no longer a thread. It is just an object, at that point, like all
other objects.
But, there are design patterns, for making a pool of threads that you can keep using to
perform different jobs. But you do not do it by restarting a dead thread.
Summary
Synchronized methods prevent more than one thread from accessing an object’s
critical method code simultaneously.
You can use the synchronized keyword asa method modifier, or to start a
synchronized block of code.
To synchronize a block of code, you mustspecify an argument that is the object
whose lock you want to synchronize on.
While only one thread can be accessing synchronized code of particular instance,
multiple threads can still access the same object’s unsynchronized code.
Static methods can be synchronized, using the lock from the java.lang.Class instance
representing that class.
After completing this session, you will be able to:
Describe Thread Synchronization
Explain lifecycle of threads
Identify wait() method and notify() method of the Object class
Race Condition and How to Solve it
Race conditions occur when multiple, asynchronously executing threads access the
same object (called a shared resource) returning unexpected (wrong) results.
Example: Threads often need to share a common resource that is a file, with one
thread reading from the file while another thread writes to the file.
They can be avoided by synchronizing the threads, which access the shared resource.
An Unsynchronized Example
1 class TwoStrings {
2 static void print(String str1, String str2) {
3 System.out.print(str1);
4 try {
5 Thread.sleep(500);
6 } catch (InterruptedException ie) {
7 }
8 System.out.println(str2);
9 }
10 }
11
12 class PrintStringsThread implements Runnable {
13 Thread thread;
14 String str1, str2;
15 PrintStringsThread(String str1, String str2) {
16 this.str1 = str1;
17 this.str2 = str2;
18 thread = new Thread(this);
19 thread.start();
20 }
21 public void run() {
22 TwoStrings.print(str1, str2);
23 }
24 }
25
26 class TestThread {
27 public static void main(String args[]) {
28 new PrintStringsThread("Hello ", "there.");
29 new PrintStringsThread("How are ", "you?");
30 new PrintStringsThread("Thank you ",
31 "very much!");
32 }
33 }
Sample output:
Hello How are Thank you there.
you?
very much!
Synchronization: Locking an Object
A thread is synchronized by becoming an owner of the monitor of the object: Consider it as locking
an object
A thread becomes the owner of the monitor of the object in one of following ways:
Option 1: Use synchronized method
Option 2: Use synchronized statement on a common object
Option 1: Use Synchronized Method
1 class TwoStrings {
2 synchronized static void print(String str1,
3 String str2) {
4 System.out.print(str1);
5 try {
6 Thread.sleep(500);
7 } catch (InterruptedException ie) {
8 }
9 System.out.println(str2);
10 }
11 }
12
13 class PrintStringsThread implements Runnable {
14 Thread thread;
15 String str1, str2;
16 PrintStringsThread(String str1, String str2) {
17 this.str1 = str1;
18 this.str2 = str2;
19 thread = new Thread(this);
20 thread.start();
21 }
22 public void run() {
23 TwoStrings.print(str1, str2);
24 }
25 }
26
27 class TestThread {
28 public static void main(String args[]) {
29 new PrintStringsThread("Hello ", "there.");
30 new PrintStringsThread("How are ", "you?");
31 new PrintStringsThread("Thank you ",
32 "very much!");
33 }
34 }
Option 1: Executing Synchronized Method
Sample output:
Hello there.
How are you?
Thank you very much!
Option 2: Use Synchronized Statement on a Common Object
1 class TwoStrings {
2 static void print(String str1, String str2) {
3 System.out.print(str1);
4 try {
5 Thread.sleep(500);
6 } catch (InterruptedException ie) {
7 }
8 System.out.println(str2);
9 }
10 }
11
12 class PrintStringsThread implements Runnable {
13 Thread thread;
14 String str1, str2;
15 TwoStrings ts;
16 PrintStringsThread(String str1, String str2,
17 TwoStrings ts) {
18 this.str1 = str1;
19 this.str2 = str2;
20 this.ts = ts;
21 thread = new Thread(this);
22 thread.start();
23 }
24
25 public void run() {
26 synchronized (ts) {
27 ts.print(str1, str2);
28 }
29 }
30 }
31 class TestThread {
32 public static void main(String args[]) {
33 TwoStrings ts = new TwoStrings();
34 new PrintStringsThread("Hello ", "there.", ts);
35 new PrintStringsThread("How are ", "you?", ts);
36 new PrintStringsThread("Thank you ",
37 "very much!", ts);
38 }
39 }
wait() Method of Object Class
wait() method causes a thread to release the lock it is holding on an object allowing
another thread to run.
wait() method is defined in the Object class.
wait() can only be invoked from within synchronized code.
It should always be wrapped in a try block as it throws IOException.
wait() can only be invoked by the thread that owns the lock on the object.
When wait() is called, the thread becomes disabled for scheduling and lies dormant
until one of the four things occur:
Another thread invokes the notify() method for this object and the scheduler arbitrarily
chooses to run the thread.
Another thread invokes the notifyAll() method for this object.
Another thread interrupts this thread.
The specified wait() time elapses.
When one of the earlier occurs, the thread becomes re-available to the Thread
scheduler and competes for a lock on the object
Once it regains the lock on the object, everything resumes as if no suspension had
occurred.
notify() Method
notify() method wakes up a single thread thatis waiting on the monitor of this object:
If any threads are waiting on this object, then one of them is chosen to be awakened.
The choice is arbitrary and occurs at the discretion of the implementation.
notify() method can only be used within synchronized code.
The awakened thread will not be able to proceed until the current thread relinquishes
the lock on this object.
Try It Out:-
Problem Statement:
Write a program that illustrates the usage of synchronized statement in thread synchronization.
Code:
class ThreadSynchronizedStmtApp extends Thread {
static String message[] = { "Java", "is", "hot,", "aromatic,",
"and",
"invigorating." };
public static void main(String args[]) {
ThreadSynchronizedStmtApp thread1 = new
ThreadSynchronizedStmtApp("thread1: ");
ThreadSynchronizedStmtApp thread2 = new
ThreadSynchronizedStmtApp("thread2: ");
thread1.start();
thread2.start();
boolean thread1IsAlive = true;
boolean thread2IsAlive = true;
How It Works:
The synchronized statement is similar to a synchronized method in that. It is used to
acquire a lock on an object before performing an action.
The synchronized statement differs from a synchronized method in that. It can be used
with the lock of any object. The synchronized method can only be used with the lock of
object or class.
It also differs in a way that it applies toa statement block, rather than an entire
method.
The syntax of the synchronized statement is as follows:
synchronized (object) {
statement(s)
}
The statements enclosed by the braces are only executed when the current thread
acquires the lock for the object or class enclosed by parentheses.
Tips and Tricks:
Can you reuse a Thread object? Can you give it a newjob to do and then restart it by calling start()
again?
Solution:
No. Once the run() method of a thread has completed, the thread can never be
restarted. In fact, at that point the thread moves into the dead state. In the dead state,
the thread has finished its run() method and can never be restarted. The Thread object
might still be on the heap, as a living object that you can call other methods on (if
appropriate), but the Thread object has permanently lost its ‘threadness’. In other
words, the Thread object is no longer a thread. It is just an object, at that point, like all
other objects.
But, there are design patterns, for making a pool of threads that you can keep using to
perform different jobs. But you do not do it by restarting a dead thread.
Summary
Synchronized methods prevent more than one thread from accessing an object’s
critical method code simultaneously.
You can use the synchronized keyword asa method modifier, or to start a
synchronized block of code.
To synchronize a block of code, you mustspecify an argument that is the object
whose lock you want to synchronize on.
While only one thread can be accessing synchronized code of particular instance,
multiple threads can still access the same object’s unsynchronized code.
Static methods can be synchronized, using the lock from the java.lang.Class instance
representing that class.