Threads in Java - Easy Approach

You might also like

Download as ppt, pdf, or txt
Download as ppt, pdf, or txt
You are on page 1of 40

Threads

Lesson’s Objectives

By the end of this lesson you will:


• Be familiar with the Java threads syntax and API
• Be able to create Java multithreaded applications

2
Agenda • Threads Overview

• Creating threads in Java

• Synchronization

• wait() and notify()

• Some Theory about Threads


Concurrent Programming Using
Java Threads
• Motivation
– Efficiency
• Downloading network data files
– Convenience
• A clock icon
– Multi-client applications
• HTTP Server, SMTP Server
• Caution
– Significantly harder to debug and maintain
• Two Main Approaches:
– Make a self-contained subclass of Thread with the
behavior you want
– Implement the Runnable interface and put behavior
in the run method of that object
Threads Overview

– Threads allow the program to run tasks in


parallel
– In many cases threads need to be synchronized,
that is, be kept not to handle the same data in
memory concurrently
– There are cases in which a thread needs to wait
for another thread before proceeding

5
Thread Mechanism One: Making a
Thread Subclass
• Create a separate subclass of Thread
– No import statements needed: Thread is in java.lang
• Put the actions to be performed in the run
method of the subclass
– public void run() { … }
• Create an instance of your Thread subclass
– Or lots of instances if you want lots of threads
• Call that instance’s start method
– You put the code in run, but you call start!
Threads in Java

Option 1 – extending class Thread:


public class Thread1 extends Thread {

@Override
public void run() {
System.out.println("Thread1 ThreadId: " +
Thread.currentThread().getId());
// do our thing
PrintNumbers.printNumbers();

}
}

7
Threads in Java

The operation we want to be threaded:


public class PrintNumbers {
static public void printNumbers() {
for(int i=0; i<1000; i++) {
System.out.println(
Thread.currentThread().getId() +
": " + i);
}
}
}

8
Threads in Java

Option 1 – extending class Thread (cont’):

static public void main(String[] args) {


System.out.println("Main ThreadId: " +
Thread.currentThread().getId());
for(int i=0; i<3; i++) {
new Thread1().start(); // don't call run!
// (if you want a separate thread)
}
PrintNumbers.printNumbers();
}

9
Thread Mechanism Two:
Implementing Runnable
• Put the actions to be performed in the run
method of your existing class
• Have class implement Runnable interface
– If your class already extends some other class (e.g.,
Applet), why can't it still extend Thread? Because Java
does not support multiple inheritance.
• Construct an instance of Thread passing in
the existing object (i.e., the Runnable)
– Thread t = new Thread(theRunnableObject);
• Call that Thread’s start method
– t.start();

10
Threads in Java

Option 2 – implementing Runnable:


public class Thread2 implements Runnable {

@Override
public void run() {
System.out.println("Thread2 ThreadId: " +
Thread.currentThread().getId());
// do our thing
PrintNumbers.printNumbers();
}
}

11
Threads in Java

Option 2 – implementing Runnable (cont’):

static public void main(String[] args) {


System.out.println("Main ThreadId: " +
Thread.currentThread().getId());
for(int i=0; i<3; i++) {
new Thread(new Thread2()).start();
// again, don't call run!
// (if you want a separate thread)
}
PrintNumbers.printNumbers();
}

12
Race Condition
A race condition is an undesirable situation that
occurs when a device or system attempts to perform
two or more operations at the same time, but because
of the nature of the device or system, the operations
must be done in the proper sequence to be done
correctly.

13
Synchronization

Synchronization of threads is needed for in order


to control threads coordination, mainly in order to
prevent simultaneous operations on data i.e. to
avoid Race Condition
For simple synchronization Java provides the
synchronized keyword
For more sophisticated locking mechanisms,
starting from Java 5, the package
java.concurrent.locks provides additional locking
options.

14
Race Conditions: Example (Continued)
Race Conditions: Example (Continued)
Synchronization

Example 1 – synchronizing methods:


public class SynchronizedCounter {
private int c = 0;
public synchronized void increment() { c++; }
public synchronized void decrement() { c--; }
public synchronized int value() { return c; }
}

The synchronized keyword on a method means


that if this is already locked anywhere
(on this method or elsewhere) by another thread,
we need to wait till this is unlocked before
entering the method

17
Synchronization

Example 2 – synchronizing blocks:


public void addName(String name) {
synchronized(this) {
lastName = name;
nameCount++;
}
nameList.add(name);
}

When synchronizing a block, key for the locking


should be supplied (usually would be this)
The advantage of not synchronizing the entire
method is efficiency

18
Synchronization

Example 3 – synchronizing using different locks:


public class TwoCounters {
private long c1 = 0, c2 = 0;
private Object lock1 = new Object();
private Object lock2 = new Object();
public void inc1() {
synchronized(lock1) {
c1++; You must be
} absolutely sure
} that there is no tie
public void inc2() { between c1 and c2
synchronized(lock2) {
c2++;
}
}
}
19
Synchronization

Example 4 – synchronizing static methods:


public class Screen {
private static Screen theScreen;
private Screen(){…} // private c’tor
public static synchronized getScreen() {
if(theScreen == null) {
theScreen = new Screen();
} This is a
return theScreen; Singleton
} example
}

20
Synchronization

Example 4 – synchronizing static methods …

Having a static method be synchronized means that


ALL objects of this type are locked on the method
and can get in one thread at a time.
The lock is the Class object representing this class.
The performance penalty might be sometimes too
high – needs careful attention!

21
Synchronization

Example 4’ – a better singleton:


public class Screen {
private static Screen theScreen = new Screen();
private Screen(){…} // private c’tor
public static getScreen() {
return theScreen;
}
} No
synchronization

22
wait(), notify(), notifyAll()

wait() and notify() allows a thread to


wait for an event

A call to notifyAll() allows all threads that are on


wait() with the same lock to be released
A call to notify() allows one arbitrary thread that is
on a wait() with the same lock to be released

Instead of
“busy wait” or
sleep loop!

23
Producer Consumer Example

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);
p1.start();
c1.start();
}
}

24
Producer Consumer Example
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();
}}

25
Producer Consumer Example
class Consumer extends Thread {
private CubbyHole cubbyhole;
private int number;
public Consumer(CubbyHole c, int number) {
cubbyhole = c;
this.number = number;
}
public void run() {
int value = 0;
for (int i = 0; i < 10; i++) {
value = cubbyhole.get();
System.out.println("Consumer #" + this.number + " got: " + value);
}
}
}

26
Producer Consumer Example
class Producer extends Thread {
private CubbyHole cubbyhole;
private int number;
public Producer(CubbyHole c, int number) {
cubbyhole = c;
this.number = number;
}
public void run() {
for (int i = 0; i < 10; i++) {
cubbyhole.put(i);
System.out.println("Producer #" + this.number + " put: " + i);
try { sleep((int)(Math.random() * 1000));
}
catch (InterruptedException e) { }
}
}
} 27
Producer Consumer Example Expected Output
Producer #1 put: 0
Consumer #1 got: 0
Producer #1 put: 1
Consumer #1 got: 1
Producer #1 put: 2
Consumer #1 got: 2
Producer #1 put: 3
Consumer #1 got: 3
Producer #1 put: 4
Consumer #1 got: 4
Producer #1 put: 5
Consumer #1 got: 5
Producer #1 put: 6
Consumer #1 got: 6
Producer #1 put: 7
Consumer #1 got: 7
Producer #1 put: 8
Consumer #1 got: 8
Producer #1 put: 9
28
Consumer #1 got: 9
Thread Lifecycle
start()

new

I/O completed
ready
notify()
times expires
or blocked
dispatch
interrupted

waiting yield()
sleeping
sleep()

Block on I/O
running

run completes
dead
Useful Thread Constructors
• Thread()
– Default version you get when you call constructor of your
custom Thread subclass.
• Thread(Runnable target)
– Creates a thread, that, once started, will execute the run
method of the target
• Thread(ThreadGroup group,
Runnable target)
– Creates a thread and places it in the specified thread group
– A ThreadGroup is a collection of threads that can be
operated on as a set
• Thread(String name)
– Creates a thread with the given name
Thread Priorities
• A thread’s default priority is the same as the
creating thread
• Thread API defines three thread priorities
• Thread.MAX_PRIORITY (typically 10)
• Thread.NORM_PRIORITY (typically 5)
• Thread.MIN_PRIORITY (typically 1)
• threadobject.setPriority(number);
• Problems
– A Java thread priority may map differently to the thread
priorities of the underlying OS
• Solaris has 232–1 priority level; Windows has 7 user
priority levels
– Starvation can occur for lower-priority threads if the higher-
priority threads never terminate, sleep, or wait for I/O
Useful Thread Methods
• start
– Initializes the thread and then calls run
– If the thread was constructed by providing a
Runnable, then start calls the run method of
that Runnable
• run
– The method in which a created thread will
execute
– Do not call run directly; call start on the thread
object
– When run completes the thread enters a dead
state and cannot be restarted
Useful Thread Methods
(Continued)
• wait
– Releases the lock for other threads and suspends itself
(placed in a wait queue associated with the lock)
– Thread can be restarted through notify or notifyAll
– These methods must be synchronized on the lock object of
importance

• notify/notifyAll
– Wakes up all threads waiting for the lock
– A notified doesn’t begin immediate execution, but is placed in
the runnable thread queue
Useful Thread Methods
(Continued)
• sleep
– Causes the currently executing thread to do a
nonbusy wait for at least the amount of time
(milliseconds), unless interrupted
– As a static method, may be called for nonthreaded
applications as well
• I.e., anyone can call Thread.sleep
• Note that sleep throws InterruptedException. Need
try/catch
• yield
– Allows any other threads of the same or higher
priority to execute (moves itself to the end of the
priority queue)
– If all waiting threads have a lower priority, then the
yielding thread remains on the CPU
Useful Thread Methods (Continued)
• currentThread
– Returns a reference to the currently executing thread
– This is a static method that can be called by arbitrary
methods, not just from within a Thread object
• I.e., anyone can call Thread.currentThread
• interrupt
– One of two outcomes:
• If the thread is executing join, sleep, or wait, an
InterruptedException is thrown
• Sets a flag, from which the interrupted thread can check
(isInterrupted)
• interrupted
– Checks whether the currently executing thread has a request for
interruption (checks flag) and clears the flag
Useful Thread Methods (Continued)
public class ThreadInterruptExample implements Runnable {

public void run() {


for (int i = 1; i <= 10; i++) {
System.out.println("This is message #" + i);

try {
Thread.sleep(2000);
continue;
} catch (InterruptedException ex) {
System.out.println("I'm resumed");
}
}
}
public static void main(String[] args) {
Thread t1 = new Thread(new ThreadInterruptExample());
t1.start();
try {
Thread.sleep(5000);
t1.interrupt();
} catch (InterruptedException ex) {
// do nothing
}

}
}
Useful Thread Methods (Continued)
Useful Thread Methods
(Continued)
• isInterrupted
– Simply checks whether the thread’s interrupt flag has
been set (does not modify the flag)
• Reset the flag by calling interrupted from
within the run method of the flagged thread
• join
– Joins to another thread by simply waiting (sleeps) until
the other thread has completed execution
• isDaemon/setDaemon
– Determines or set the thread to be a daemon
– A Java program will exit when the only active threads
remaining are daemon threads
Useful Thread Methods
(Continued)
public class ThreadJoinExample implements Runnable {
public void run() {
for (int i = 1; i <= 10; i++) {
System.out.println("This is message #" + i);

try {
Thread.sleep(2000);
} catch (InterruptedException ex) {
System.out.println("I'm about to stop");
return;
}
}
}
public static void main(String[] args) {
Thread t1 = new Thread(new ThreadJoinExample());
t1.start();
try {
t1.join();
} catch (InterruptedException ex) {
// do nothing
}
System.out.println("I'm " + Thread.currentThread().getName());
}
}
Summary
• Achieve multithreaded behavior by
– Inheriting directly from Thread
(separate class approach)
– Implementing the Runnable interface (callback
approach)
• In either case, put your code in the run
method. Call start on the Thread object.
• Avoid race conditions by placing the shared
resource in a synchronized block
• You can’t restart a dead thread
• Stop threads by setting a flag that the
thread's run method checks

You might also like