Synchronization
Unit 4
Using isAlive() and join()
• The method isAlive() is used to determine whether the thread is still
running or not. It takes the following general form:
final boolean isAlive()
• The method returns true if the thread upon which it is called is still
running. It returns false otherwise.
• The join() method waits until the thread on which it is called
terminates. Its name comes from the concept of the calling thread
waiting until the specified thread joins it.
• Additional forms of join( ) allow you to specify a maximum amount
of time that you want to wait for the specified thread to terminate.
• The following example is used to demonstrate the use of join() to
ensure that the main thread is the last to stop. It also demonstrates
the isAlive( ) method
Example
// Using join() to wait for threads to finish
class NewThread implements Runnable
{
String name; // name of thread
Thread t;
NewThread(String threadname)
{
name = threadname;
t = new Thread(this, name);
System.out.println("New thread: " + t);
t.start(); // Start the thread
} // This is the entry point for thread.
public void run()
{
Prog.,
{
try
{
for(int i = 5; i > 0; i--)
{
System.out.println(name + ": " + i);
Thread.sleep(1000);
}
}
catch (InterruptedException e)
{
System.out.println(name + " interrupted.");
}
System.out.println(name + " exiting."); }
}
class DemoJoin
{
public static void main(String args[])
{
NewThread ob1 = new NewThread("One");
NewThread ob2 = new NewThread("Two");
NewThread ob3 = new NewThread("Three");
Prog.,
System.out.println("Thread One is alive: " + ob1.t.isAlive());
System.out.println("Thread Two is alive: " + ob2.t.isAlive());
System.out.println("Thread Three is alive: " + ob3.t.isAlive()); // wait for threads to finish try {
System.out.println("Waiting for threads to finish.");
ob1.t.join();
ob2.t.join();
ob3.t.join();
}
catch (InterruptedException e)
{
System.out.println("Main thread Interrupted");
}
System.out.println("Thread One is alive: " + ob1.t.isAlive());
System.out.println("Thread Two is alive: " + ob2.t.isAlive());
System.out.println("Thread Three is alive: " + ob3.t.isAlive());
System.out.println("Main thread exiting.");
}
}
Output
New thread: Thread[One,5,main]
Three: 3
New thread: Thread[Two,5,main]
One: 2
New thread: Thread[Three,5,main]
Two: 2
Thread One is alive: true
Thread Two is alive: true Three: 2
Thread Three is alive: true One: 1
Waiting for threads to finish. Two: 1
One: 5 Three: 1
Two: 5
Two exiting.
Three: 5
One: 4 Three exiting.
Two: 4 One exiting.
Three: 4 Thread One is alive: false
One: 3 Thread Two is alive: false
Two: 3
Thread Three is alive: false
Main thread exiting.
Synchronization
• When two or more threads need access to a shared
resource, they need some way to ensure that the
resource will be used by only one thread at a time.
• The process by which this is achieved is called
synchronization. Java provides unique, language-level
support for it. Key to synchronization is the concept of
the monitor (also called a semaphore).
• A monitor is an object that is used as a mutually
exclusive lock, or mutex. Only one thread can own a
monitor at a given time. When a thread acquires a lock,
it is said to have entered the monitor.
• All other threads attempting to enter the
locked monitor will be suspended until the
first thread exits the monitor.
• These other threads are said to be waiting for
the monitor. A thread that owns a monitor can
reenter the same monitor if it so desires.
Using Synchronized Methods
• Synchronization is easy in Java, because all
objects have their own implicit monitor
associated with them. To enter an object’s
monitor, just call a method that has been
modified with the synchronized keyword.
• While a thread is inside a synchronized
method, all other threads that try to call it (or
any other synchronized method) on the same
instance have to wait.
This program is not synchronized.
class Caller implements Runnable
class Callme {
{ String msg;
void call(String msg) Callme target;
{
System.out.print("[" + msg); Thread t;
try public Caller(Callme targ, String s)
{ {
Thread.sleep(1000); target = targ;
} msg = s;
catch(InterruptedException e)
{ t = new Thread(this);
System.out.println("Interrupted"); t.start();
} }
System.out.println("]"); public void run()
} {
}
target.call(msg);
}
}
class Synch
{ Here is the output produced
public static void main(String args[]) by this program:
{ Hello[Synchronized[World]
Callme target = new Callme();
Caller ob1 = new Caller(target, "Hello"); ]
Caller ob2 = new Caller(target, "Synchronized"); ]
Caller ob3 = new Caller(target, "World"); // wait for threads to end
try
{
ob1.t.join();
ob2.t.join();
ob3.t.join();
}
catch(InterruptedException e)
{
System.out.println("Interrupted");
}
}
The synchronized Statement
• Using synchronized methods within classes to
achieve synchronization is easy and effective
but it will not work in all cases.
• Consider the following case.
• Imagine that you want to synchronize access
to objects of a class that was not designed for
multithreaded access. That is, the class does
not use synchronized methods.
• This is the general form of the synchronized statement:
synchronized(object)
{
// statements to be synchronized
}
• Here, object is a reference to the object being
synchronized. A synchronized block ensures that a call to a
method that is a member of object occurs only after the
current thread has successfully entered object’s monitor.
• Here is an alternative version of the preceding example,
using a synchronized block within the run( ) method:
This program uses a synchronized
block.
class Callme class Caller implements Runnable
{ {
void call(String msg) String msg;
{ Callme target;
System.out.print("[" + msg); Thread t;
try public Caller(Callme targ, String s)
{ { target = targ;
Thread.sleep(1000); msg = s;
} t = new Thread(this);
catch (InterruptedException e) t.start();
{ System.out.println("Interrupted"); } // synchronize calls to call()
} System.out.println("]"); public void run()
{
}
synchronized(target)
}
{ // synchronized block
target.call(msg);
}
}
}
class Synch1
{ Output:
public static void main(String args[])
{ [Hello]
Callme target = new Callme(); [Synchronized]
Caller ob1 = new Caller(target, "Hello"); [World]
Caller ob2 = new Caller(target, "Synchronized");
Caller ob3 = new Caller(target, "World"); // wait for threads to end try
{
ob1.t.join();
ob2.t.join();
ob3.t.join();
}
catch(InterruptedException e)
{ System.out.println("Interrupted");
}
}
}
Inter-thread Communication
• The preceding examples unconditionally blocked other
threads from asynchronous access to certain methods.
• This use of the implicit monitors in Java objects is
powerful, but we can achieve a more subtle level of
control through inter-process communication.
• Multithreading replaces event loop programming by
dividing our tasks into discrete, logical units. Threads
also provide a secondary benefit: they do away with
polling.
• Polling is usually implemented by a loop that is used to
check some condition repeatedly. Once the condition is
true, appropriate action is taken. This wastes CPU time
• For example, consider the classic queuing problem,
where one thread is producing some data and another
is consuming it. Suppose that the producer has to wait
until the consumer is finished before it generates more
data.
• In a polling system, the consumer would waste many
CPU cycles while it waited for the producer to produce.
• Once the producer was finished, it would start polling,
wasting more CPU cycles waiting for the consumer to
finish, and so on. Clearly, this situation is undesirable.
• To avoid polling, Java includes an elegant inter-process
communication mechanism via the wait( ), notify( ), and
notifyAll( ) methods. These methods are implemented as
final methods in Object, so all classes have them. All three
methods can be called only from within a synchronized
context.
• wait( ) tells the calling thread to give up the monitor and go
to sleep until some other thread enters the same monitor
and calls notify( ).
• notify( ) wakes up a thread that called wait( ) on the same
object.
• notifyAll( ) wakes up all the threads that called wait( ) on
the same object. One of the threads will be granted access.
These methods are declared within Object, as
shown here:
• final void wait( ) throws InterruptedException
• final void notify( )
• final void notifyAll( )
• It consists of four classes: Q, the queue that
you’re trying to synchronize;
• Producer, the threaded object that is
producing queue entries;
• Consumer, the threaded object that is
consuming queue entries; and PC, the tiny
class that creates the single Q, Producer, and
Consumer
An incorrect implementation of a
producer and consumer
class Q Q q;
{ Producer(Q q)
int n; {
synchronized int get() this.q = q;
{
new Thread(this, "Producer").start();
System.out.println("Got: " + n);
return n; }
} public void run()
synchronized void put(int n) {
{ int i = 0;
this.n = n;
while(true)
System.out.println("Put: " + n);
} {
} q.put(i++);
class Producer implements Runnable }
{ }
}
Prog.,
class Consumer implements Runnable
{
Q q; class PC
Consumer(Q q)
{
{
this.q = q; public static void main(String args[])
new Thread(this, "Consumer").start();
}
{
public void run() Q q = new Q();
{ new Producer(q);
while(true)
{ q.get(); new Consumer(q);
} System.out.println("Press Control-C to
}
}
stop.");
}
}
Output:
Put: 1
As evident, after the producer put 1, the
Got: 1
consumer started and got the same 1 five
Got: 1 times in a row. Then, the producer resumed
Got: 1 and produced 2 through 7 without letting
Got: 1 the consumer have a chance to consume
Got: 1 them. The proper way to write this program
Put: 2 in Java is to use wait( ) and notify( ) to signal
Put: 3 in both directions, as shown here:
Put: 4
Put: 5
Put: 6
Put: 7
Got: 7
A correct implementation of a
producer and consumer.
class Q System.out.println("Got: " + n);
{ valueSet = false;
int n; notify();
boolean valueSet = false; return n;
synchronized int get() }
synchronized void put(int n)
{ while(!valueSet)
{
try
while(valueSet)
{
try
wait(); {
} wait();
catch(InterruptedException e) }
{ catch(InterruptedException e)
System.out.println("InterruptedException caught"); {
} System.out.println("InterruptedException caught");
}
this.n = n;
Prog.,
valueSet = true; class Consumer implements Runnable
System.out.println("Put: " + n);
{
notify();
Q q; Consumer(Q q)
}
{ this.q = q;
}
new Thread(this, "Consumer").start();
class Producer implements Runnable
}
{ Q q;
public void run()
Producer(Q q)
{
{
while(true) { q.get();
this.q = q;
}
new Thread(this, "Producer").start();
}
}
}
public void run()
class PCFixed
{
{
int i = 0;
while(true) public static void main(String args[])
{ {
q.put(i++); Q q = new Q();
} new Producer(q);
} new Consumer(q);
} System.out.println("Press Control-C to stop.");
}
}
Output
Put: 1
Got: 1
Put: 2
Got: 2
Put: 3
Got: 3
Put: 4
Got: 4
Put: 5
Got: 5
Deadlock
• A special type of error that we need to avoid that relates specifically to
multitasking is deadlock, which occurs when two threads have a circular
dependency on a pair of synchronized objects.
• For example, suppose one thread enters the monitor on object X and
another thread enters the monitor on object Y.
• If the thread in X tries to call any synchronized method on Y, it will block as
expected. However, if the thread in Y, in turn, tries to call any synchronized
method on X, the thread waits forever, because to access X, it would have
to release its own lock on Y so that the first thread could complete.
• Deadlock is a difficult error to debug for two reasons:
• In general, it occurs only rarely, when the two threads time-slice in just the
right way.
• It may involve more than two threads and two synchronized objects. (That
is, deadlock can occur through a more convoluted sequence of events.)
Suspending, resuming, and
stopping threads in Java
• Java's threads allow for concurrent processing of multiple
requests. Java includes functionality to pause, resume, and
terminate running threads. Using these functions, you can
manage thread execution and guarantee that it proceeds
normally.
• So, in this post, I'll go over how to suspend, resume, and
terminate threads in the Java programming language.
However, if you are unfamiliar with multithreading in Java,
it is recommended that you learn it first and then proceed
to understand how thread suspending, resuming, and
stopping work.
Suspending Threads in Java
• In Java, a thread can be suspended by using
the wait() method on an object. This method
suspends thread execution until it is notified
by another thread using the notify() method.
As an example:
Suspending Threads in Java
Resuming Threads in Java
• An interrupted thread's execution can be
picked back up by notifying the waiting thread
using the notify() method. As an example:
Stopping Threads in Java
• To halt a running thread, use a boolean flag to signal the
thread to stop gracefully. Consider the following code
fragment as an example: