Presentation is loading. Please wait.

Presentation is loading. Please wait.

CS203 Lecture 15.

Similar presentations


Presentation on theme: "CS203 Lecture 15."— Presentation transcript:

1 CS203 Lecture 15

2 2 Multithreading Multithreading is just what it sounds like: concurrent running of multiple tasks. Multithreading may be implemented either: With parallel execution, in which multiple operations are executed simultaneously. This requires multiple CPUs. Without parallel execution, in a context in which multiple threads have access to the same CPU at different times

3 Threads Concept Multiple threads on multiple CPUs
3 Multiple threads on multiple CPUs Multiple threads sharing a single CPU

4 4 Multithreading Multithreading has been available for a long time, even when it was unusual to run an application on multiple processors. Multithreading offers a way to run multiple tasks at once without blocking, ie making tasks wait for another task which is waiting for user input, for some resource to become available, for a set amount of time to pass, etc. Multithreading offers a way to give higher-priority tasks access to resources when needed without starving lower- priority ones Event-driven programming with GUI components often uses multiple threads, some of which are waiting for user input. Multithreading also makes it easier for an operating system or other scheduling mechanism to spread work among different processors. This is increasingly important as multiple cores have become the norm and as distributed applications, ones running on multiple computers on a network, become more prevalent.

5 Creating Tasks and Threads
5 Creating Tasks and Threads Write a class that implements Runnable Instantiate a Thread, sending an instance of your Runnable implementation to the constructor Call start() on the Thread 5

6 6 The Thread Class

7 7 Multithreading Example from Liang textbook follows. This example sets up several threads that produce various output and runs them simultaneously. Before we take any steps to control how the output from the different threads is synchronized, the order of the output depends on aspects of the operating system and hardware that we can't predict. Run the original code several times, and you will probably see different output.

8 8 public class TaskThreadDemo {
public static void main(String[] args) { // Create tasks Runnable printA = new PrintChar('a', 100); Runnable printB = new PrintChar('b', 100); Runnable print100 = new PrintNum(100); // Create threads Thread thread1 = new Thread(printA); Thread thread2 = new Thread(printB); Thread thread3 = new Thread(print100); // Start threads thread1.start(); thread2.start(); thread3.start(); } // The task for printing a specified character in specified times class PrintChar implements Runnable { private char charToPrint; // The character to print private int times; // The times to repeat /** Construct a task with specified character and number of * times to print the character */ public PrintChar(char c, int t) { charToPrint = c; times = t; @Override /** Override the run() method to tell the system * what the task to perform public void run() { for (int i = 0; i < times; i++) { System.out.print(charToPrint); // The task class for printing number from 1 to n for a given n class PrintNum implements Runnable { private int lastNum; /** Construct a task for printing 1, 2, ... i */ public PrintNum(int n) { lastNum = n; @Override /** Tell the thread how to run */ for (int i = 1; i <= lastNum; i++) { System.out.print(" " + i); 8

9 The Static sleep(milliseconds) Method
9 The Static sleep(milliseconds) Method The sleep(long mills) method puts the thread to sleep for the specified time in milliseconds. For example, consider this code: int lastNum = 15; for (int i = 1; i <= lastNum; i++) { System.out.print(" " + i); try { Thread.sleep(i* i * i); } catch (InterruptedException ex) { } Every time a number i is printed, the current thread is put to sleep for i3 milliseconds.

10 10 Context Switch When a CPU switches between threads, it must access the memory used by the new thread and change the contents of the processor cache (very fast on-chip memory.) This is called context switching. Since context switching is expensive, the OS has extensive rules for scheduling threads. The actual results depend, among many other things, on what else is occurring on the computer. It is not usually practical to predict how processors will be scheduled. Add a Thread.sleep(1); to the run() methods in the inner classes in the demo and the threads will come closer to taking turns, as the delay makes it more likely that the scheduler has has given the CPU to another thread.

11 The Static yield() Method
11 The Static yield() Method You can use the yield() method to temporarily release time for other threads. For example, suppose you modify the run() method of PrintNum in TaskThreadDemo.java as follows: public void run() { for (int i = 1; i <= lastNum; i++) { System.out.print(" " + i); Thread.yield(); } Every time a number is printed, the print100 thread is yielded. No more than one number is printed at a time as long as the other threads have not completed.

12 12 The join() Method You can use the join() method to force one thread to wait for another thread to finish. For example, suppose you modify the run() of PrintNum as follows and comment out the other threads: public void run() { Runnable printA = new PrintChar('a', 200); Thread thread2 = new Thread(printA); thread2.start(); for (int i = 1; i <= lastNum; i++) { System.out.print(" " + i); if (i >= 2) try { thread2.join(); } catch (InterruptedException e) { e.printStackTrace(); } The numbers after 2 are printed after thread printA is finished.

13 Multiple Cores and Multithreading
13 Multiple Cores and Multithreading Assignment of your threads to threads in the OS kernel, which in turn are assigned to particular processors, is generally unpredictable. This differs from one OS to another, and there are many other threads also running at any time. You will learn more about this in CS4440. Try the following example with one thread, two threads, and three threads, etc, and check your CPU usage. You will probably find that, while total usage is high, you can’t see any obvious patterns related to the number of threads in use.

14 Multiple Cores and Multithreading
14 Multiple Cores and Multithreading package week8; public class CPUBurner implements Runnable { long a = 0; @Override public void run() { while (true) { a++; }

15 15 Multiple Cores and Multithreading package week8; public class CPUBurnerDemo { public static void main(String[] args){ Runnable b1 = new CPUBurner(); Runnable b2 = new CPUBurner(); Runnable b3 = new CPUBurner(); Thread t1 = new Thread(b1); Thread t2 = new Thread(b2); Thread t3 = new Thread(b3); t1.start(); t2.start(); t3.start(); } **Use the red stop button in Eclipse to terminate this after you get bored!!!** If you forget to close an app in Eclipse, you can get a list of open apps by clicking on the monitor screen icon in the output panel, choosing the zombie app, and clicking on the red button.

16 Thread Life Cycle: States
16 Thread Life Cycle: States Threads may be in any of the following states: New (not started) Ready (scheduled by OS, not yet running) Runnable Waiting (say, for input) Timed Waiting (Thread.sleep) Blocked (by CPU scheduler, for cases where another thread must execute first) Terminated

17 isAlive(), interrupt(), and isInterrupted()
17 isAlive(), interrupt(), and isInterrupted() The isAlive() method is used to find out the state of a thread. It returns true if a thread is in the Ready, Blocked, or Running state; it returns false if a thread is new and has not started or if it is finished. The interrupt() method interrupts a thread in the following way: If a thread is currently in the Ready or Running state, its interrupted flag is set; if a thread is currently blocked, it is awakened and enters the Ready state, and an java.io.InterruptedException is thrown. The isInterrupted() method tests whether the thread is interrupted.

18 Life Cycle Demo 18 package week8; import javax.swing.JOptionPane;
public class LifeCycleThread implements Runnable { int a = 0; @Override public void run() { int quit; do { try { Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } quit = JOptionPane.showConfirmDialog(null, "Quit?"); } while(quit != 0);

19 Life Cycle Demo 19 package week8;
public class LimitedCPUBurner implements Runnable { long a = 0; @Override public void run() { while(true){ a++; try { Thread.sleep(1000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); }

20 20 package week8; import java.lang.Thread.State;
public class LifeCycleDemo { public static void main(String[] args){ Runnable b1 = new CPUBurner(); Runnable l = new LifeCycleThread(); Runnable b2 = new LimitedCPUBurner(); Thread t1 = new Thread(b1); Thread t2 = new Thread(l); Thread t3 = new Thread(b2); System.out.println("Thread 1 is " + t1.getState()); t1.start(); //t2.start(); System.out.println("Thread 1 is " + (t1.isAlive()?"alive":"dead")); System.out.println("Thread 1 is " + (t1.isInterrupted()?"":"not ") +"interrupted"); System.out.println("Interrupting t1"); t1.interrupt(); t2.start(); t3.start(); System.out.println("Thread 3 is " + t3.getState()); do { try { Thread.sleep(1000); System.out.println("Thread 2 is " + t2.getState()); } catch (InterruptedException e) { e.printStackTrace(); } } while(t2.getState() != State.TERMINATED); t2 = null; System.exit(0); 20

21 Thread Priority 21 Each thread is assigned a default priority of Thread.NORM_PRIORITY. You can reset the priority using setPriority(int priority). Some constants for priorities include Thread.MIN_PRIORITY Thread.MAX_PRIORITY Thread.NORM_PRIORITY The results of setting priorities are still not fully predictable and are OS-dependent. OS has complex ways of deciding when threads get the CPU and output. Experiment with the next demo and you will see setting priorities makes less difference than you might expect.

22 public class ThreadPriorityDemo {
22 package week8; public class ThreadPriorityDemo { public static void main(String[] args) { // Create tasks Runnable printA = new MyPrintChar('a', 100); Runnable printB = new MyPrintChar('b', 100); Runnable print100 = new MyPrintNum(100); // Create threads Thread thread1 = new Thread(printA); Thread thread2 = new Thread(printB); Thread thread3 = new Thread(print100); thread1.setPriority(Thread.MIN_PRIORITY ); thread3.setPriority(Thread.MAX_PRIORIT Y); // Start threads thread1.start(); thread2.start(); thread3.start(); } // The task for printing a specified character in specified times class MyPrintChar implements Runnable { private char charToPrint; // The character to print private int times; // The times to repeat /** Construct a task with specified character and number of * times to print the character */ public MyPrintChar(char c, int t) { charToPrint = c; times = t; @Override /** Override the run() method to tell the system * what the task to perform public void run() { for (int i = 0; i < times; i++) { System.out.print(charToPrint); // The task class for printing number from 1 to n for a given n class MyPrintNum implements Runnable { private int lastNum; /** Construct a task for printing 1, 2, ... i */ public MyPrintNum(int n) { lastNum = n; @Override /** Tell the thread how to run */ for (int i = 1; i <= lastNum; i++) { System.out.print(" " + i);

23 GUI Event Dispatcher Thread
23 GUI Event Dispatcher Thread GUI event handling and painting code executes in a single thread, called the event dispatcher thread. This ensures that each event handler finishes executing before the next one executes and the painting isn’t interrupted by events. 23

24 24 Thread Pools Starting a new thread for each task could limit throughput and cause poor performance as the OS juggles a huge number of threads. A thread pool provides a limited number of threads and allocates them to tasks, which are usually held in a queue. The JDK uses the Executor interface for executing tasks in a thread pool and the ExecutorService interface for managing and controlling tasks. ExecutorService is a subinterface of Executor.

25 25 Creating Executors To create an Executor object, use the static methods in the Executors class in the java.util.concurrent package.

26 26 package week8; import java.util.concurrent.*;
public class ExecutorDemo { public static void main(String[] args) { // Create a fixed thread pool with maximum three threads ExecutorService executor = Executors.newFixedThreadPool(3); // Submit runnable tasks to the executor executor.execute(new PrintChar('a', 100)); executor.execute(new PrintChar('b', 100)); executor.execute(new PrintChar('c', 100)); executor.execute(new PrintChar('d', 100)); executor.execute(new PrintChar('e', 100)); executor.execute(new PrintChar('f', 100)); executor.execute(new PrintChar('g', 100)); // Shutdown the executor executor.shutdown(); } // The thread class for printing a specified character // in specified times private static class PrintChar implements Runnable { private char charToPrint; // The character to print private int times; // The times to repeat /** Construct a thread with specified character and number of times to print the character */ public PrintChar(char c, int t) { charToPrint = c; times = t; 26

27 27 /** Override the run() method to tell the system
what the thread will do */ public void run() { for (int i = 0; i < times; i++) System.out.print(charToPrint); } // The thread class for printing number from 1 to n for a given n private static class PrintNum implements Runnable { private int lastNum; /** Construct a thread for print 1, 2, ... i */ public PrintNum(int n) { lastNum = n; /** Tell the thread how to run */ for (int i = 1; i <= lastNum; i++) System.out.print(" " + i);

28 Thread Synchronization
28 Thread Synchronization A shared resource may be corrupted if it is accessed simultaneously by multiple threads. For example, two unsynchronized threads accessing the same bank account may cause conflict.

29 29 29 package week8; import java.util.concurrent.*;
public class AccountWithoutSync { private static Account account = new Account(); public static void main(String[] args) { ExecutorService executor = Executors.newCachedThreadPool(); // Create and launch 100 threads for (int i = 0; i < 100; i++) { executor.execute(new AddAPennyThread()); } executor.shutdown(); // Wait until all tasks are finished while (!executor.isTerminated()) { System.out.println("What is balance ? " + account.getBalance()); // A thread for adding a penny to the account private static class AddAPennyThread implements Runnable { public void run() { account.deposit(1); // An inner class for account private static class Account { private int balance = 0; public int getBalance() { return balance; public void deposit(int amount) { int newBalance = balance + amount; // This delay is deliberately added to magnify the // data-corruption problem and make it easy to see. try { Thread.sleep(1); catch (InterruptedException ex) { ex.printStackTrace(); balance = newBalance; 29 29

30 30 Race Condition The effect of this scenario is that Task 1 did nothing, because in Step 4 Task 2 overrides Task 1's result. Task 1 and Task 2 are accessing a common resource in a way that causes conflict. This is a common problem known as a race condition in multithreaded programs. A class is said to be thread-safe if an object of the class does not cause a race condition in the presence of multiple threads. As demonstrated in the preceding example, the Account class is not thread-safe. 30

31 The synchronized keyword
31 The synchronized keyword To avoid race conditions, we must avoid allowing more than one thread to simultaneously enter certain parts of the program, known as critical regions. The critical region in the Listing 29.7 is the deposit method. You can use the synchronized keyword to synchronize the method so that only one thread can access the method at a time. One way to correct the problem with Account is to make it thread-safe by adding the synchronized keyword in the deposit method as follows:   public synchronized void deposit(int amount) 31

32 Synchronizing Instance Methods and Static Methods
32 Synchronizing Instance Methods and Static Methods With the deposit method synchronized, the preceding scenario cannot happen. If Task 2 starts to enter the method, and Task 1 is already in the method, Task 2 is blocked until Task 1 finishes the method. 32

33 Synchronizing Instance Methods and Static Methods
33 Synchronizing Instance Methods and Static Methods A synchronized method must acquire a lock before it executes. For an instance method, the lock is on the object for which the method was invoked. For a static method, the lock is on the class. If one thread invokes a synchronized instance method (respectively, static method) on an object, the lock of that object (respectively, class) is acquired first, then the method is executed, and finally the lock is released. Another thread invoking the same method of that object (respectively, class) is blocked until the lock is released. 33

34 Synchronizing Statements
34 Synchronizing Statements A synchronized statement can be used to acquire a lock on any object, not just this object, when executing a block of the code in a method. This block is referred to as a synchronized block. The general form of a synchronized statement is as follows: synchronized (expr) { statements; } The expression expr must be an object reference. If the object is already locked by another thread, the thread is blocked until the lock is released. When a lock is obtained on the object, the statements in the synchronized block are executed, and then the lock is released. 34

35 Synchronizing Statements vs. Methods
35 Synchronizing Statements vs. Methods Any synchronized instance method can be converted into a synchronized statement. Suppose that the following is a synchronized instance method: public synchronized void xMethod() { // method body } This method is equivalent to public void xMethod() { synchronized (this) { 35

36 36 Deadlock Threads must sometimes wait for particular resources, such as the CPU or output hardware, to become available. A deadlock occurs when multiple threads are waiting for resources held by the others in an order that cannot be resolved. The simplest form of deadlock occurs when a process or thread enters a waiting state because a resource requested is being held by another waiting process, which in turn is waiting for a resource that the current thread is holding. 36

37 Deadlock 37 37 package week8; public class Player implements Runnable{
public enum Resource {BAT, BALL}; private Resource resourceToBring; private Resource resourceToAwait; private DeadlockDemo d; public Player(Resource resourceToBring, Resource resourceToAwait, DeadlockDemo dIn){ this.resourceToBring = resourceToBring; this.resourceToAwait = resourceToAwait; d = dIn; } @Override public void run() { do{ System.out.println("Player with " + resourceToBring.toString() + " waiting for somebody to bring a " + resourceToAwait.toString()); try { Thread.sleep(500); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } while(!d.getResources().contains(resourceToAwait)); System.out.println("Ready to play"); 37

38 Deadlock 38 38 package week8; import java.util.ArrayList;
import java.util.List; import week8.Player.Resource; public class DeadlockDemo { private List<Resource> resources = new ArrayList<Resource>(); public List<Resource> getResources() { return resources; } public static void main(String[] args) { DeadlockDemo d = new DeadlockDemo(); Thread t1 = new Thread(new Player(Resource.BALL, Resource.BAT, d)); Thread t2 = new Thread(new Player(Resource.BAT, Resource.BALL, d)); t1.start(); t2.start(); 38


Download ppt "CS203 Lecture 15."

Similar presentations


Ads by Google