Chapter 3 Multithreading
Chapter 3 Multithreading
Threads share the process's resources, including memory and open files.
Every application has at least one thread — or several, if you count system
threads that do things like memory management and signal handling.
But from the application programmer's point of view, you start with just one
thread, called the main thread.
This thread has the ability to create additional threads, as we'll demonstrate
in the next section.
2. The Main Thread
When a Java program starts up, one thread begins running immediately.
This is called the main thread of your program, because it is the one that is
executed when your program begins.
The main thread is important for two reasons:
It is the thread from which other “child” threads will be spawned.
Often it must be the last thread to finish execution because it performs
various shutdown actions.
Although the main thread is created automatically when your program is
started, it can be controlled through a Thread object.
To do so, you must obtain a reference to it by calling the method
currentThread() of Thread class:
static Thread currentThread( )
This method returns a reference to the thread in which it is called.
Once you have a reference to the main thread, you can control it just like
any other thread.
public static void main(String args[]) {
Thread t = [Link]();
[Link]("Current thread: " + t);
//change the name of the thread
[Link]("Main Thread");
[Link]("After name change: " + t);
try {
for (int n = 5; n > 0; n--) {
[Link](n);
[Link](1000);
}
} catch (InterruptedException e) {
[Link]("Main thread interrupted");
}
}
• When thread t is converted to string, it returns a thread name, its priority & thread group name.
Output:
Current thread: Thread[main,5,main]
After name change: Thread[Main Thread,5,main]
5
4
3
2
1
3. Creating a Thread
An application that wants to create a thread must provide the
code that will run in that thread.
There are two ways to create a thread:
Extending the Thread class
Implementing the Runnable interface
To create a thread, your program has to either extend Thread
or implement the Runnable interface.
3. Creating a Thread…
A. Thread Class
The Thread class lets you create an object that can be run as a thread in a
multi-threaded Java application.
One way to create a thread is to create a class that extends Thread class.
The extending class must override the run() method, which is the entry point
for the new thread.
It must also call start() to begin execution of the new thread.
Commonly used constructors of Thread class:
Thread()
Thread(String name)
Thread(Runnable r)
Thread(Runnable r, String name)
Thread(ThreadGroup group, Runnable r)
Thread(ThreadGroup group, Runnable r, String name)
3. Creating a Thread…
The Thread class defines several methods that help manage threads.
Method Meaning
static int activeCount() Returns the number of active threads.
Fills the specified array with a copy of each active thread. The
static int enumerate(Thread[] t)
return value is the number of threads added to the array.
String getName() Returns the name of the thread.
int getPriority() Returns the thread’s priority.
void interrupt() Interrupts this thread.
boolean interrupted() Checks to see if the thread has been interrupted.
void setPriority(int priority) Sets the thread’s priority.
void setName(String name) Sets the thread’s name.
static void sleep(int Causes the currently executing thread to sleep for the specified
milliseconds) number of milliseconds.
This method is called when the thread is started. Place the code
void run()
that you want the thread to execute inside this method.
void start() Starts the thread.
This causes the current thread to move from the running state to
static void yield()
the ready state, so that other threads may get a chance to run.
3. Creating a Thread…
Example: creating simple thread
public class HelloThread extends Thread {
public void run() {
[Link]("Hello from a thread!");
}
public static void main(String args[]) {
HelloThread ht = new HelloThread();
[Link]();
}
}
When the start method is called, the thread starts execution by executing the
code inside the run() method of the thread.
The run() method is not called directly by programs rather called internally
by the thread itself.
All we have to do to start a thread is call the start() method.
3. Creating a Thread…
Java assigns every thread a priority.
Thread priorities are used by the thread scheduler to decide when each
thread should be allowed to run.
In theory, higher-priority threads get more CPU time than lower-priority
threads.
By default, a thread inherits the priority of the thread that spawned it.
You can set priority of any thread by using the setPriority(int) method, and
you can get the thread’s priority by using the getPriority() method.
Priorities are numbers ranging from 1 to 10.
The Thread class has the int constants MIN_PRIORITY, NORM_PRIORITY, and
MAX_PRIORITY, representing 1, 5, and 10, respectively.
The priority of the main thread is Thread.NORM_PRIORITY.
The JVM always picks the currently runnable thread with the highest priority.
A lower priority thread can run only when no higher-priority threads are
running.
If all runnable threads have equal priorities, each is assigned an equal
portion of the CPU time in a circular queue.
Example: setting thread priority
public class ThreadPriority {
public static void main(String args[]) {
Thread tt1 = new OneThread("1");
Thread tt2 = new OneThread("2");
[Link](8);
[Link](2);
[Link]();
[Link]();
}
}
class OneThread extends Thread {
String name;
OneThread(String nm) {
name = nm;
}
public void run() {
for (int i = 1; i <= 50; i++) {
[Link]("Child Thread" + name + ": " + i);
}
[Link]("Exiting child thread " + name + ".");
}
}
3. Creating a Thread…
B. Runnable Interface
The easiest way to create a thread is to create a class that
implements the Runnable interface.
Runnable abstracts a unit of executable code.
The run() method can call other methods, use other classes, and
declare variables, just like the main thread can.
The only difference is that run() establishes the entry point for
another, concurrent thread of execution within your program.
This thread will end when run() returns.
3. Creating a Thread…
To run it, you will instantiate an object of type Thread passing the object of
the class that implements the Runnable interface as parameter.
Thread defines several constructors for this.
Thread(Runnable threadObj)
Thread(Runnable threadOb, String threadName)
In the constructor, threadObj is an instance of a class that implements the
Runnable interface.
This defines where execution of the thread will begin.
The name of the new thread is specified by threadName.
After the new thread is created, it will not start running until you call its
start() method, which is declared within Thread.
In essence, start() executes a call to run().
void start()
Example: creating thread using Runnable
class TestThread implements Runnable {
public void run() {
try {
for(int i = 5; i > 0; i--) {
[Link]("Child Thread: " + i);
[Link](500);
}
} catch (InterruptedException e) {
[Link]("Child interrupted.");
}
[Link]("Exiting child thread.");
}
}
class ThreadDemo {
public static void main(String args[]) {
Thread tt = new Thread(new TestThread());
[Link]();
try {
for(int i = 5; i > 0; i--) {
[Link]("Main Thread: " + i);
[Link](1000);
}
} catch (InterruptedException e) {
[Link]("Main thread interrupted.");
}
[Link]("Main thread exiting.");
}
}
Output: (the output on your computer may differ)
Child thread: Thread[Demo Thread,5,main]
Main Thread: 5
Child Thread: 5
Child Thread: 4
Main Thread: 4
Child Thread: 3
Child Thread: 2
Main Thread: 3
Child Thread: 1
Exiting child thread.
Main Thread: 2
Main Thread: 1
Main thread exiting.
3. Creating a Thread…
Which approach is better?
Of the two ways to create threads, which approach is better?
The answers to that question turn on the same point.
The Thread class defines several methods that can be overridden by a
derived class.
Of these methods, the only one that must be overridden is run().
This is, of course, the same method required when you implement Runnable.
Many Java programmers feel that classes should be extended only when
they are being enhanced or modified in some way.
So, if you will not be overriding any of Thread’s other methods, it is
probably best simply to implement Runnable.
Also, by implementing Runnable, your thread class does not need to inherit
Thread, making it free to inherit from other class.
Ultimately, which approach to use is up to you.
4. Controlling Thread
I. Pausing Execution with Sleep
[Link](length) causes the current thread to suspend execution for a
specified period.
This is an efficient means of making processor time available to the other
threads or other applications.
The sleep method can also be used for pacing, and waiting for another
thread with duties that are understood to have time requirements.
Two overloaded versions of sleep are provided:
However, these sleep times are not guaranteed to be precise, because they
are limited by the facilities provided by the underlying OS.
Also, the sleep period can be terminated by interrupts.
So, you cannot assume that invoking sleep will suspend the thread for
precisely the time period specified.
Example: main thread uses sleep to print messages at four-second intervals:
public class SleepMessage extends Thread{
public void run(){
String info[] = {"Mares eat oats", "Dogs eat oats", "Little lambs eat ivy",
"A kid will eat ivy too"};
for (int i = 0; i < [Link]; i++) {
try {
[Link](4000); //sleep 4 seconds
[Link](info[i]);
} catch (InterruptedException ex) {
[Link]();
}
}
}
public static void main(String args[]) throws InterruptedException {
SleepMessage sm = new SleepMessage();
[Link]();
}
}
4. Controlling Thread…
II. Interrupts
An interrupt is an indication to a thread that it should stop what it is doing and
do something else.
It's up to the programmer to decide exactly how a thread responds to an
interrupt, but it is very common for the thread to terminate.
A thread sends an interrupt by invoking interrupt() on the Thread object for the
thread to be interrupted.
For the interrupt mechanism to work correctly, the interrupted thread must
support its own interruption.
How to support interrupt depends on what it's currently doing.
If the thread is frequently invoking methods that throw InterruptedException, it
simply returns from the run method after it catches that exception.
4. Controlling Thread…
public void run() {
for (int i = 0; i < [Link]; i++) {
try {
[Link](4000);
} catch (InterruptedException e) {
//thread is interrupted: no more messages.
return;
}
[Link](info[i]);
}
}
Many methods that throw InterruptedException, such as sleep, are designed to
cancel their current operation and return immediately when an interrupt is
received.
What if a thread goes a long time without invoking a method that
throws InterruptedException?
Then it must periodically invoke [Link]() method, which
returns true if an interrupt has been received.
4. Controlling Thread…
For example:
public void run() {
for (int i = 0; i < [Link]; i++) {
heavyCrunch(inputs[i]);
if ([Link]()) {
//thread is interrupted: no more crunching.
return;
}
}
}
4. Controlling Thread…
III. Joins
It is not uncommon for one thread to need the result of another thread.
For example, a web browser loading an HTML page in one thread might create
a second thread to retrieve every image embedded in the page.
Java provides three join() methods to allow one thread to wait for another
thread to finish before continuing.
These are:
void join() throws InterruptedException
void join(long milliseconds) throws InterruptedException
void join(long milliseconds, int nanoseconds) throws InterruptedException
The first variant waits indefinitely for the joined thread to finish.
The second two variants wait for the specified amount of time, after which they
continue even if the joined thread has not finished.
As with the sleep() method, nanosecond accuracy is not guaranteed.
4. Controlling Thread…
The joining thread (i.e., the one that executes the join() method) waits for the
joined thread (i.e, the one whose join() method is invoked) to finish.
The thread on which the join() is executed is said to have joined the thread
whose name is used to call the join() method.
The join() method allows one thread to wait for the completion of another.
The join() method waits for a thread to die.
In other words, it causes the currently running thread to stop executing until the
thread it joins with completes its task.
class TestThread implements Runnable {
String name;
TestThread(String nm) {
name = nm;
}
public void run() {
try {
for (int i = 5; i > 0; i--) {
[Link]("Child Thread" + name + ": " + i);
[Link](1000);
}
} catch (InterruptedException e) {
[Link]("Child interrupted.");
}
[Link]("Exiting child thread " + name + ".");
}
}
class ThreadDemo {
public static void main(String args[]) {
Thread tt1 = new Thread(new TestThread("1"));
[Link]();
try {
[Link](); Output:
} catch (InterruptedException ex) { Child Thread1: 5
Child Thread1: 4
[Link]("Thread interrupted");
Child Thread1: 3
} Child Thread1: 2
Thread tt2 = new Thread(new TestThread("2")); Child Thread1: 1
[Link](); Exiting child thread 1.
Main Thread: 5
for (int i = 5; i > 0; i--) {
Main Thread: 4
[Link]("Main Thread: " + i); Main Thread: 3
} Main Thread: 2
[Link]("Main thread exiting."); Main Thread: 1
Main thread exiting.
} Child Thread2: 5
} Child Thread2: 4
Child Thread2: 3
Child Thread2: 2
Child Thread2: 1
Exiting child thread 2.
4. Controlling Thread…
IV. Inter-thread communication: wait(), notify() and notifyAll()
The Object class in Java has three final methods that allow threads to
communicate about the locked status of a resource.
A. wait()
It tells the calling thread to give up the lock and go to sleep until
some other thread enters the same monitor and calls notify().
The wait() method releases the lock prior to waiting and reacquires
the lock prior to returning from the wait() method.
The wait() method is actually tightly integrated with the
synchronization lock, using a feature not available directly from the
synchronization mechanism.
In other words, it is not possible for us to implement the wait() method
purely in Java: it is a native method.
4. Controlling Thread…
General syntax for calling wait() method is like this:
synchronized( lockObject ) {
while( ! condition ) {
[Link]();
}
//take the action here;
}
B. notify()
It wakes up one single thread that called wait() on the same object.
It should be noted that calling notify() does not actually give up a lock on a
resource.
It tells a waiting thread that that thread can wake up.
However, the lock is not actually given up until the notifier’s synchronized
block has completed.
4. Controlling Thread…
General syntax for calling notify() method is like this:
synchronized(lockObject) {
//establish_the_condition;
[Link]();
//any additional code if needed
}
C. notifyAll()
It wakes up all the threads that called wait() on the same object.
The highest priority thread will run first in most of the situation, though not
guaranteed.
Other things are same as notify() method above.
synchronized(lockObject) {
establish_the_condition;
[Link]();
}
class Customer { class Test {
int amount = 10000; public static void main(String args[]) {
synchronized void withdraw(int amount) { final Customer c = new Customer();
[Link]("going to withdraw..."); Thread t1 = new Thread() {
if ([Link] < amount) { public void run() {
[Link]("Less balance, [Link](15000);
waiting for deposit"); }
try { };
wait(); [Link]();
} catch (Exception e) {} Thread t2 = new Thread() {
} public void run() {
[Link] -= amount; [Link](10000);
[Link]("withdraw completed..."); }
} };
synchronized void deposit(int amount) { [Link]();
[Link]("going to deposit..."); }
[Link] += amount; }
[Link]("deposit completed... ");
notify();
}
5. Thread States: Life Cycle of a Thread
synchronized statements.
6. Synchronization…
A. Synchronized Methods
To avoid thread interference and memory consistency errors, it is necessary
to prevent more than one thread from simultaneously entering a certain part
of the program, known as the critical region.
You can use the keyword synchronized to synchronize the method so that
only one thread can access the method at a time.
Synchronized methods is a simple strategy for preventing thread
interference and memory consistency errors.
Synchronization is easy in Java, because all objects have their own implicit
object 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 on the same instance have to wait.
To exit the monitor & relinquish control of the object to another waiting
thread, the owner of monitor simply returns from the synchronized method.
To make a method synchronized, simply add the synchronized keyword to its
declaration:
public class Counter {
private int c = 0;
public synchronized void increment() {
c++;
}
public synchronized void decrement() {
c--;
}
}
If count is an instance of Counter, then making these methods synchronized
has two effects:
First, it is not possible for two invocations of synchronized methods on the
same object to interleave.
When one thread is executing a synchronized method for an object, all other
threads that invoke synchronized methods for the same object blocks until the
first thread is done with the object.
Second, when a synchronized method exits, it automatically establishes a
happens-before relationship with any subsequent invocation of a
synchronized method for the same object.
This guarantees that changes to the state of the object are visible to all
threads.
Example: using synchronized methods
public class Counter {
private int c = 0;
public synchronized void increment() {
c++;
}
public synchronized void decrement() {
c--;
}
public synchronized int value() {
return c;
}
}
class CounterThread extends Thread {
static Counter sc = new Counter();
String name;
public CounterThread(String nn) {
name = nn;
}
public void run() {
for(int i = 0; i < 10; i++) {
[Link]();
[Link]("Thread " + name + ": " + [Link]());
}
for(int i = 0; i < 5; i++) {
[Link]();
[Link]("Thread " + name + ": " + [Link]());
}
}
}
class SyncTest {
public static void main(String args[]) {
CounterThread ct1 = new CounterThread("1");
CounterThread ct2 = new CounterThread("2");
[Link]();
[Link]();
}
}
A synchronized method acquires a lock before it executes.
In the case of an instance method, the lock is on the object for which the
method was invoked.
In the case of a static method, the lock is on the class.
If one thread invokes a synchronized instance method on an object, the lock
of that object is acquired first, then the method is executed, and finally the
lock is released.
6. Synchronization…
B. Synchronized Statements
Another way to create synchronized code is with synchronized statements.
synchronized(object) {
// statements to be synchronized
}
Here, object is a reference to the object being synchronized.
occurs only after the current thread has successfully entered object’s monitor.
6. Synchronization…
Unlike synchronized methods, synchronized statements must specify the
object that provides the intrinsic lock:
class Person {
String lastName;
static int nameCount = 0;
ArrayList nameList;
public Person() {
nameList = new ArrayList();
}
public void addName(String name) {
synchronized(this) {
lastName = name;
nameCount++;
}
[Link](name);
}
}
6. Synchronization…
Synchronized statements are also useful for improving concurrency with fine-
grained synchronization.
Suppose, for example, class MsCounter has two instance fields, c1 and c2,
that are never used together.
All updates of these fields must be synchronized, but there's no reason to
prevent an update of c1 from being interleaved with an update of c2 —
and doing so reduces concurrency by creating unnecessary blocking.
Instead of using synchronized methods or otherwise using the lock associated
with this, we create two objects solely to provide locks.
6. Synchronization…
public class MsCounter {
private long c1 = 0, c2 = 0;
private Object lock1 = new Object();
private Object lock2 = new Object();
public void inc1() {
synchronized(lock1) {
c1++;
}
}
public void inc2() {
synchronized(lock2) {
c2++;
}
}
}
Use this approach with extreme care.
You must be absolutely sure that it really is safe to interleave access of the affected
fields.
7. Deadlock
A special type of error that you need to avoid that relates specifically to
multitasking is deadlock.
Deadlock occurs when two threads have a circular dependency on a pair of
synchronized objects.
Deadlock describes a situation where two or more threads are blocked
forever, waiting for each other.
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.
class A {
synchronized void foo(B b) {
String name = [Link]().getName();
[Link](name + " trying to call [Link]()");
[Link]();
}
synchronized void last() {
[Link]("Inside [Link]");
}
}
class B {
synchronized void bar(A a) {
String name = [Link]().getName();
[Link](name + " trying to call [Link]()");
[Link]();
}
synchronized void last() {
[Link]("Inside [Link]");
}
}
public class DeadLock implements Runnable {
A a = new A();
B b = new B();
DeadLock() {
[Link]().setName("MainThread");
Thread t = new Thread(this, "RacingThread");
[Link]();
[Link](b); // get lock on a in this thread.
[Link]("Back in main thread");
}
public void run() {
[Link](a); // get lock on b in other thread.
[Link]("Back in other thread");
}
public static void main(String args[]) {
new DeadLock();
}
}
This produces the following output and goes into deadlock:
RacingThread trying to call [Link]()
MainThread trying to call [Link]()
Deadlock is easily avoided by using a simple technique known as resource ordering.
With this technique, you assign an order to all the objects whose locks must be
acquired and ensure that each thread acquires the locks in that order.
7. Deadlock…
Producer/Consumer Problem
The producer-consumer problem (also known as the bounded-buffer
problem) is another classical example of a multithread synchronization
problem.
The problem describes two threads, the producer and the consumer, who
share a common, fixed-size buffer.
The producer’s job is to generate a piece of data and put it into the buffer.
The consumer is consuming the data from the same buffer simultaneously.
The problem is to make sure that the producer will not try to add data into
the buffer if it is full and that the consumer will not try to remove data from
an empty buffer.
The solution for this problem involves two parts.
The producer should wait when it tries to put the newly created product into
the buffer until there is at least one free slot in the buffer.
The consumer, on the other hand, should stop consuming if the buffer is
empty.
7. Deadlock…
To synchronize the operations, use a lock with two conditions:
notEmpty (i.e., buffer is not empty) and
When a task adds an int to the buffer, if the buffer is full, the task will wait
for the notFull condition.
When a task deletes an int from the buffer, if the buffer is empty, the task
will wait for the notEmpty condition.
public class ProducerConsumerTest {
public static void main(String[] args) {
CubbyHole c = new CubbyHole();
Producer p1 = new Producer(c, 1);
Consumer c1 = new Consumer(c, 1);
[Link]();
[Link]();
}
}
class CubbyHole {
private int contents;
private boolean available = false;
public synchronized int get() {
while (available == false) {
try {
wait();
} catch (InterruptedException e) {}
}
available = false;
notifyAll();
return contents;
}
public synchronized void put(int value) {
while (available == true) {
try {
wait();
} catch (InterruptedException e) { }
}
contents = value;
available = true;
notifyAll();
}
}
class Consumer extends Thread {
private CubbyHole cubbyhole;
private int number;
public Consumer(CubbyHole c, int num) {
cubbyhole = c;
number = num;
}
public void run() {
int value = 0;
for (int i = 0; i < 10; i++) {
value = [Link]();
[Link]("Consumer #" + [Link] + " got: " + value);
}
}
}
class Producer extends Thread {
private CubbyHole cubbyhole;
private int number;
public Producer(CubbyHole c, int number) {
cubbyhole = c;
[Link] = number;
}
public void run() {
for (int i = 0; i < 10; i++) {
[Link](i);
[Link]("Producer #" + [Link] + " put: " + i);
try {
[Link]((int)([Link]() * 100));
} catch (InterruptedException e) { }
}
}
}