java threads - teilar.gr

37
Java Threads

Upload: others

Post on 04-Feb-2022

9 views

Category:

Documents


0 download

TRANSCRIPT

Page 1: Java Threads - teilar.gr

Java Threads

Page 2: Java Threads - teilar.gr

Parallel Tasks (concurrent programming A modern OS works in multi-tasking: it runs

multiple tasks in a quasi-parallel way. In reality every task is run only for a short time

and then the OS switch context to another task.

Page 3: Java Threads - teilar.gr

Different Tasks: Processes and Threads. There are two basic types of tasks:

a process is the typical program, with its own address space on the computer memory

a thread is a single sequential flow of operations within a process. Multiple threads in the same process share the same address space.

Then, parallel processing can be done by using multiple processes or multiple threads.

Page 4: Java Threads - teilar.gr

What are the Advantages of Threads? Using multiple threads has two

advantages over spawning multiple processes: There is no need for a Inter-Process

Communication (IPC) method, because they share the same address space

Depending on the OS, spawning a new process may be an expensive operation, compared to creating a thread.

Page 5: Java Threads - teilar.gr

What are the Disadvantages?

Multithreading trades off an increase of the complexity for an increase in performance.

Poor design can lead to a unrewarded cost in terms of complexity.

Further drawbacks: Slowdown due to access of shared resources. CPU overhead for managing threads. Thread-specific bugs which are difficult to find. Inconsistencies across platforms.

Use threads carefully!

Page 6: Java Threads - teilar.gr

When should Threads be used?

When a subtask waits for some event or resource, which is not needed by the rest of the program.

Examples: separating CPU-intensive

data processing from UI handling.

separating I/O subtasks from main task.

Page 7: Java Threads - teilar.gr

Basic Java Thread Programming

Page 8: Java Threads - teilar.gr

Thread class

Basic way to use threads: Inherit from java.lang.Thread Put your code in the run() method Start the thread with the start() method

Page 9: Java Threads - teilar.gr

A Simple Thread Example public class SimpleThread extends Thread { private int countDown = 5; private static int threadCount = 0; public SimpleThread() { super("" + ++threadCount); // Store the thread name start(); // Start at construction } public String toString() { return "#" + getName() + ": " + countDown; } public void run() { while(true) { System.out.println(this); if(--countDown == 0) // Break condition return; } } public static void main(String[] args) { for(int i = 0; i < 5; i++) new SimpleThread(); } }

Page 10: Java Threads - teilar.gr

Threads order of execution

The order of execution of multiple threads is decided by the JVM scheduler.

However, the Thread class has some methods to tune the scheduler behaviour: yield(): may switch to another thread sleep(): puts the thread to sleep setPriority(): changes the thread priority join(): waits for another thread to finish

Page 11: Java Threads - teilar.gr

Yielding

In the run() method you can give a hint to the scheduler to switch to another thread using the yield() method.

It’s only a hint: there is no guarantee the scheduler will switch!

Page 12: Java Threads - teilar.gr

A Simple Thread Example public class YieldingThread extends Thread { private int countDown = 5; private static int threadCount = 0; public YieldingThread() { super("" + ++threadCount); // Store the thread name start(); // Start at construction } public String toString() { return "#" + getName() + ": " + countDown; } public void run() { while(true) { System.out.println(this); if(--countDown == 0) // Break condition return; yield(); // Switch thread (maybe) } } public static void main(String[] args) { for(int i = 0; i < 5; i++) new YieldingThread(); } }

Page 13: Java Threads - teilar.gr

Sleeping

An alternative to the yield() method is the sleep() method: it stops thread execution for a given number of milliseconds.

sleep() must be put in a try block because it can be interrupted before it times out.

As with yield() there is no deterministic control of the order of execution: it only guarantees that the thread will sleep at least the given number of milliseconds.

Page 14: Java Threads - teilar.gr

sleep() example public class SleepingThread extends Thread { private int countDown = 5; private static int threadCount = 0; public SleepingThread() { super("" + ++threadCount); // Store the thread name start(); // Start at construction } public String toString() { return "#" + getName() + ": " + countDown; } public void run() { while(true) { System.out.println(this); if(--countDown == 0) // Break condition return; try { sleep(100); // Sleep at least 100 ms } catch (InterruptedException e) { throw new RuntimeException(e); } } } public static void main(String[] args) { new SleepingThread(); } }

Page 15: Java Threads - teilar.gr

Priority

The priority of a thread tells how often the thread is run: If there are a number of threads waiting to be run, the

scheduler will favor the one with highest priority. Lower priority threads tends to be run less often (no

deadlock because of priorities) Priorities are accessed with setPriority() and

getPriority()

Page 16: Java Threads - teilar.gr

public class SimplePriorities extends Thread { private int countDown = 5; private volatile double d = 0; // No optimization public SimplePriorities(String name, int priority) { super(name); setPriority(priority); start(); // Start at construction } public String toString() { return getName() + ": " + countDown; } public void run() { while(true) { // An expensive, interruptable operation for (int i = 1; i < 100000; i++) d = d + (Math.PI + Math.E) / (double)i; System.out.println(this); if(--countDown == 0) // Break condition return; } } public static void main(String[] args) { new SimplePriorities("high", Thread.MAX_PRIORITY); for(int i = 0; i < 5; i++) new SimplePriorities("low"+i, Thread.MIN_PRIORITY); } }

Page 17: Java Threads - teilar.gr

SimplePriorities: a possible output low0: 5 high: 5 low0: 4 high: 4 low0: 3 high: 3 low0: 2 high: 2 low0: 1 high: 1 low2: 5 low3: 5 low2: 4 low1: 5 low2: 3 low1: 4 low2: 2 low1: 3 …

Page 18: Java Threads - teilar.gr

Joining a thread

A thread can wait for the completion of another thread before starting.

If a thread calls t.join() on a thread t, it will wait until t completion.

The join() method, as sleep(), can be interrupted, so it must be in a try block.

Page 19: Java Threads - teilar.gr

join() example public class Sleeper extends Thread { private int duration; public Sleeper(int sleepTime) { duration = sleepTime; start(); } public void run() { try { sleep(duration); } catch (InterruptedException e) { System.out.println(getName() + " was interrupted"); return; } System.out.println(getName() + " has awakened"); } }

public class Joiner extends Thread { private Sleeper sleeper; public Joiner(Sleeper sleeper) { this.sleeper = sleeper; start(); } public void run() { try { sleeper.join(); } catch (InterruptedException e) { throw new RuntimeException(e); } System.out.println(getName() + " join completed"); } public static void main(String[] args) { new Joiner(new Sleeper(3000)); } }

Page 20: Java Threads - teilar.gr

Daemon Threads

If a thread is defined as daemon, it does not prevent the program to terminate.

The daemon status of a thread must be set with setDaemon() before starting it.

isDeamon() returns the status.

Page 21: Java Threads - teilar.gr

Daemon Threads Example public class SimpleDaemons extends Thread { public SimpleDaemons() { setDaemon(true); start(); // Start at construction } public void run() { while(true) { try { sleep(100); // Sleep a little bit } catch (InterruptedException e) { throw new RuntimeException(e); } System.out.println(this); } } public static void main(String[] args) { for (int i = 0; i < 10; ++i) new SimpleDaemons(); } }

The program will terminate immediately because there are no non-daemon threads!

Page 22: Java Threads - teilar.gr

Inheritance and Threads

If your thread class must inherit from another class you cannot extend Thread, because Java does not support multiple inheritance.

In this case you have to: declare Runnable as an interface of your class implement a run() method construct a Thread object passing your class as

argument start the thread with the start() method

Page 23: Java Threads - teilar.gr

Runnable Interface Example public class RunnableThread implements Runnable { private int countDown = 5; public String toString() { return "#" + Thread.currentThread().getName() + ": " + countDown; } public void run() { while(true) { System.out.println(this); if(--countDown == 0) return; } } public static void main(String[] args) { for(int i = 1; i <= 5; i++) new Thread(new RunnableThread(), "" + i).start(); } }

With Thread.currentThread() you get a reference to the Thread object.

Page 24: Java Threads - teilar.gr

Sharing Resources

Page 25: Java Threads - teilar.gr

Threads and external Data When threads access common (global) data, they

require special attention. Access to the same data by two different threads can cause

conflicts. If data are changed by the second thread after the first has read

them but before it has wrote them, inconsistencies result.

Page 26: Java Threads - teilar.gr

Threads colliding over Resources public class AlwaysEven { private int i; public void next() { i++; i++; } public int getValue() { return i; // can be called anytime!!! } public static void main(String[] args) { final AlwaysEven ae = new AlwaysEven(); new Thread("Watcher") { // anonymous thread public void run() { while(true) { int val = ae.getValue(); if(val % 2 != 0) { System.out.println(val); System.exit(0); } } } }.start(); while(true) ae.next(); }

The “Watcher” thread may access i at the same time as next() is called.

Page 27: Java Threads - teilar.gr

Access Serialization

To avoid such collisions, access to shared resources is serialized: only one thread at a time can access the shared

resource. This is done by locking the resource when it is

accessed by a thread. It is a mechanism of mutual exclusion (mutex).

In Java, access to a resource is serialized by using the keyword synchronized.

Page 28: Java Threads - teilar.gr

Mutually Exclusive Access Example public class CorrectAlwaysEven { private int i; public synchronized void next() { i++; i++; } public synchronized int getValue() { return i; // can be called anytime!!! } public static void main(String[] args) { final CorrectAlwaysEven cae = new CorrectAlwaysEven(); new Thread("Watcher") { // anonymous thread public void run() { while(true) { int val = cae.getValue(); if(val % 2 != 0) { System.out.println(val); System.exit(0); } } } }.start(); while(true) cae.next(); } }

The lock is on the class CorrectAlwaysEven.

Page 29: Java Threads - teilar.gr

Synchronized If a thread calls a synchronized method of an object,

any other thread calling a synchronized method of the same object is blocked.

If a static method is synchronized, it will lock any static data (only static) of the class.

Blocks of code can also be synchronized.

class AClass { public void aMethod() { // not synchronized! System.out.println(“Not synchronized.”); synchronized(this) { System.out.println(“Synchronized.”); } } }

Page 30: Java Threads - teilar.gr

The wait() method

The wait() method can be used as sleep(), giving an optional number of milliseconds as argument, with two important differences: wait() releases the lock on the object wait() can be interrupted by a call to notify() or

notifyAll() If no argument is passed, wait() does not return

until either notify() or notifyAll() is called. This is the most common use of wait().

Page 31: Java Threads - teilar.gr

How does wait() work?

Since the wait() method release the object lock, it must be put into a synchronized block (otherwise you get an exception at run-time).

For the same reason, it is a method of the base Object class, and not of Thread.

Page 32: Java Threads - teilar.gr

Conditional synchronization

The wait(), notify() and notifyAll() methods are used to implement conditional synchronization: blocking threads until a certain condition is met.

A consumer/producer example: a Chef produces a Restaurant Order a Waiter has to wait for an Order and deliver

it

Page 33: Java Threads - teilar.gr

Example: the Waiter Class class Waiter extends Thread { private Restaurant restaurant; public Waiter(Restaurant r) { restaurant = r; start(); } public void run() { while(true) { while(restaurant.order == null) // no order synchronized(this) { try { wait(); // wait for an order } catch(InterruptedException e) { throw new RuntimeException(e); } } System.out.println( "Waitperson got " + restaurant.order); restaurant.order = null; } // end of infinite loop } }

The lock is on the Waiter itself.

Page 34: Java Threads - teilar.gr

Example: the Chef Class class Chef extends Thread { private Restaurant restaurant; private Waiter waiter; public Chef(Restaurant r, Waiter w) { restaurant = r; waiter = w; start(); } public void run() { while(true) { if(restaurant.order == null) { // no order: produce a new one restaurant.order = new Order(); System.out.print("Order up! "); synchronized(waiter) { waiter.notify(); // tells to the w. } } try { sleep(100); } catch(InterruptedException e) { throw new RuntimeException(e); } } // end of infinite loop } }

Notify is put into a synchronized block!

Page 35: Java Threads - teilar.gr

Example: the Order Class

public class Order { public Order() {} }

Page 36: Java Threads - teilar.gr

Example: the Restaurant Class public class Restaurant { public Order order; public Restaurant() { order=null; } public static void main(String[] args) { Restaurant r = new Restaurant(); Waiter w = new Waiter(r); Chef c = new Chef(r,w); } }

Page 37: Java Threads - teilar.gr

Running the Restaurant Class Order up! Waitperson got Order@11b86e7 Order up! Waitperson got Order@35ce36 Order up! Waitperson got Order@757aef Order up! Waitperson got Order@d9f9c3 Order up! Waitperson got Order@9cab16 Order up! Waitperson got Order@1a46e30 Order up! Waitperson got Order@3e25a5 Order up! Waitperson got Order@19821f Order up! Waitperson got Order@addbf1 Order up! Waitperson got Order@42e816 Order up! Waitperson got Order@9304b1 Order up! Waitperson got Order@190d11 Order up! Waitperson got Order@a90653 Order up! Waitperson got Order@de6ced Order up! Waitperson got Order@c17164 Order up! Waitperson got Order@1fb8ee3 … and so on