720 likes | 783 Views
CHAPTER 21. Multithreading. CHAPTER GOALS. To understand how multiple threads can execute in parallel To learn how to implement threads To understand race conditions and deadlocks To be able to avoid corruption of shared objects by using synchronized methods
E N D
CHAPTER 21 Multithreading
CHAPTER GOALS • To understand how multiple threads can execute in parallel • To learn how to implement threads • To understand race conditions and deadlocks • To be able to avoid corruption of shared objects by using synchronized methods • To be able to use threads for programming animations
Threads • A thread is a program unit that is executed independently of other parts of the program • The Java Virtual Machine executes each thread in the program for a short amount of time • This gives the impression of parallel execution
Steps to Running a Threads • Implement a class that extends the Thread class • Place the code for your task into the run method of your class • Create an object of your subclass • Call the start method of your class to start the thread • When a Thread object is started, the code in its run method is executed in a new thread
GreetingThread Outline • A program to print a time stamp and "Hello World" once a second for ten seconds public class GreetingThread extends Thread { public void run() { //thread action . . . } //variables used by the thread action . . . }
Thread Action for GreetingThread • Print a time stamp • Print the greeting • Wait a second
GreetingThread • We can get the date and time by constructing Date object Date now = new Date(); • To wait a second, use the sleep method of the Thread class sleep(milliseconds) • A sleeping thread can generate an InterruptedException • Catch the exception • Terminate the thread
GreetingThreadrunmethod public run() { try { //thread action } catch (InterruptedException exception) { //cleanup, if necessary } }
File GreetingThread.java 01: import java.util.Date; 02: 03: /** 04: A thread that repeatedly prints a greeting. 05: */ 06: public class GreetingThread extends Thread 07: { 08: /** 09: Constructs the thread object. 10: @param aGreeting the greating to display 11: */ 12: public GreetingThread(String aGreeting) 13: { 14: greeting = aGreeting; 15: } 16: 17: public void run()
18: { 19: try 20: { 21: for (int i = 1; i <= REPETITIONS; i++) 22: { 23: Date now = new Date(); 24: System.out.println(now + " " + greeting); 25: sleep(DELAY); 26: } 27: } 28: catch (InterruptedException exception) 29: { 30: } 31: } 32: 33: private String greeting; 34: 35: private static final int REPETITIONS = 10; 36: private static final int DELAY = 1000; 37: }
To Start the Thread • Construct an object of your thread classGreetingThread t = new GreetingThread("Hello World"); • Call the start methodt.start();
File GreetingThreadTest.java 01: import java.util.Date; 02: 03: /** 04: This program tests the greeting thread by running two 05: threads in parallel. 06: */ 07: public class GreetingThreadTest 08: { 09: public static void main(String[] args) 10: { 11: GreetingThread t1 = new GreetingThread("Hello, World!"); 12: GreetingThread t2 = new GreetingThread("Goodbye, World!"); 13: 14: t1.start(); 15: t2.start(); 16: } 17: }
Thread Scheduler • The thread scheduler runs each thread for a short amount of time called a time slice • Then the scheduler picks another thread from those that are runnable • A thread is runnable if it is not asleep or blocked in some way • There is no guarantee about the order in which threads are executed
Terminating Threads • A thread terminates when its run method returns • Do not terminate a thread using the deprecated stop method • Instead, notify a thread that it should terminate t.interrupt();
Terminating Threads • A thread's run method should check occasionally whether it has been interrupted • Use the isInterrupted method • An interrupted thread should release resources, clean up, and exit • The sleep method throws an InterruptedException when a sleeping thread is interrupted • Catch the exception • Terminate the thread
Terminating a Thread public void run( { try { for (int = 1; i <= REPETITIONS && !isInterrupted(); i++) { //do the work } } catch (InterruptedException exception) { } //cleanup }
Corrupting Data with Unsynchronized Threads • When threads share a common object, they can conflict with each other. • In this example, a DepositThread and a WithdrawThread both manipulate a single BankAccount
run Method of DepositThread public void run() { try { for (int i = 1; i <= REPETITIONS && !isInterrupted(); i++) { account.deposit(amount); sleep(DELAY); } } catch (InterruptedException exception) { } }
Sample Application • Create a BankAccount object • Create a DepositThread t0 to deposit $100 into the account for 10 iterations • Create a WithdrawThread t1 to withdraw $100 from the account for 10 iterations • The result should be zero, but sometimes it is not
Scenario to Explain Non-zero Result • The first thread t0 executes the lines System.out.print("Depositing " + amount); double newBalance = balance + amount; • t0 reaches the end of its time slice and t1 gains control • t1 calls the withdraw method which withdraws $100 from the balance variable. • Balance is now -100 • t1 goes to sleep
Scenario to Explain Non-zero Result • t0 regains control and picks up where it left off. • t0 executes the lines System.out.println(", new balance is " + newBalance); balance = newBalance; • The balance is now 100 instead of 0 because the deposit method used the OLD balance • This is called a race condition.
Race condition • Occurs if the effect of multiple threads on shared data depends on the order in which the threads are scheduled • It is possible for a thread to reach the end of its time slice in the middle of a statement. • It may evaluate the right-hand side of an equation but not be able to store the result until its next turn.
File BankAccountThreadTest.java 01: import java.util.Random; 02: 03: /** 04: This program runs two threads that deposit and withdraw 05: money from the same bank account. 06: */ 07: public class BankAccountThreadTest 08: { 09: public static void main(String[] args) 10: { 11: BankAccount account = new BankAccount(); 12: 13: DepositThread t0 = new DepositThread(account, 100); 14: WithdrawThread t1 = new WithdrawThread(account, 100); 15: 16: t0.start(); 17: t1.start();
18: } 19: } 20:
File DepositThread.java 01: /** 02: A deposit thread makes periodic deposits to a bank account. 03: */ 04: class DepositThread extends Thread 05: { 06: /** 07: Constructs a deposit thread. 08: @param anAccount the account into which to deposit money 09: @anAmount the amount to deposit in each repetition 10: */ 11: public DepositThread(BankAccount anAccount, double anAmount) 12: { 13: account = anAccount; 14: amount = anAmount; 15: } 16: 17: public void run()
18: { 19: try 20: { 21: for (int i = 1; i <= REPETITIONS && !isInterrupted(); i++) 22: { 23: account.deposit(amount); 24: sleep(DELAY); 25: } 26: } 27: catch (InterruptedException exception) 28: { 29: } 30: } 31: 32: private BankAccount account; 33: private double amount; 34: 35: private static final int REPETITIONS = 10; 36: private static final int DELAY = 10; 37: }
File WithdrawThread.java 01: /** 02: A withdraw thread makes periodic withdrawals from a bank account. 03: */ 04: class WithdrawThread extends Thread 05: { 06: /** 07: Constructs a withdraw thread. 08: @param anAccount the account from which to withdraw money 09: @anAmount the amount to withdraw in each repetition 10: */ 11: public WithdrawThread(BankAccount anAccount, double anAmount) 12: { 13: account = anAccount; 14: amount = anAmount; 15: } 16: 17: public void run()
18: { 19: try 20: { 21: for (int i = 1; i <= REPETITIONS && !isInterrupted(); i++) 22: { 23: account.withdraw(amount); 24: sleep(DELAY); 25: } 26: } 27: catch (InterruptedException exception) 28: { 29: } 30: } 31: 32: private BankAccount account; 33: private double amount; 34: 35: private static final int REPETITIONS = 10; 36: private static final int DELAY = 10; 37: }
File BankAccount.java 01: /** 02: A bank account has a balance that can be changed by 03: deposits and withdrawals. 04: */ 05: public class BankAccount 06: { 07: /** 08: Constructs a bank account with a zero balance 09: */ 10: public BankAccount() 11: { 12: balance = 0; 13: } 14: 15: /** 16: Deposits money into the bank account. 17: @param amount the amount to deposit
18: */ 19: public void deposit(double amount) 20: { 21: System.out.print("Depositing " + amount); 22: double newBalance = balance + amount; 23: System.out.println(", new balance is " + newBalance); 24: balance = newBalance; 25: } 26: 27: /** 28: Withdraws money from the bank account. 29: @param amount the amount to withdraw 30: */ 31: public void withdraw(double amount) 32: { 33: System.out.print("Withdrawing " + amount); 34: double newBalance = balance - amount; 35: System.out.println(", new balance is " + newBalance); 36: balance = newBalance; 37: }
38: 39: /** 40: Gets the current balance of the bank account. 41: @return the current balance 42: */ 43: public double getBalance() 44: { 45: return balance; 46: } 47: 48: private double balance; 49: }
Solving the Race Condition Problem • A thread must be able to lock an object temporarily • When a thread has the object locked, no other thread can modify the state of the object. • In Java, use synchronized methods to do this • Tag all methods that contain thread-sensitive code with the keyword synchronized
Synchronized Methods public class BankAccount { public synchronized void deposit(double amount) { . . . } public synchronized void withdraw(double amount) { . . . } . . . }
Synchronized Methods • By declaring both the deposit and withdraw methods to be synchronized • Our program will run correctly • Only one thread at a time can execute either method on a given object • When a thread starts one of the methods, it is guaranteed to execute the method to completion before another thread can execute a synchronized method on the same object.
Synchronized Methods • By executing a synchronized method: • The thread acquires the object lock. • No other thread can acquire the lock. • No other thread can modify the state of the object until the first thread is finished
Visualization of Synchronized Thread Behavior • Imagine the object is a restroom that only one person can use at a time • The threads are people • If the restroom is empty, a person may enter • If a second person finds the restroom locked, the second person must wait until it is empty • If multiple people want to gain access to the restroom , they all wait outside • The people may not form an orderly queue; • A randomly chosen person may gain access when the restroom becomes available again
Deadlock • A deadlock occurs if no thread can proceed because each thread is waiting for another to do some work first • BankAccount example public synchronized void withdraw(double amount) { while (balance < amount) //wait for balance to grow . . . }
Deadlock • The method can lead to deadlock • The thread sleeps to wait for balance to grow, but it still has the lock • No other thread can execute the synchronized deposit method • If a thread tries to call deposit, it is blocked until the withdraw method exits • withdraw method can't exit until it has funds available • DEADLOCK
Avoiding Deadlock • The wait method temporarily releases the object lock and deactivates the thread • Restroom analogy • Don't want the person in the restroom to go to sleep if there is no toilet paper. • Think of the person giving up and leaving • This gives another person a chance to enter and refill the toilet paper
withdraw Method to Avoid Deadlock public synchronized void withdraw(double amount) throws InterruptedException { while (balance < amount) wait(); }
Wait and NotifyAll • A thread that calls wait is in a blocked state • It will not be activated by the thread scheduler until it is unblocked • It is unblocked when another thread calls notifyAll • When a thread calls notifyAll, all threads waiting on the object are unblocked • Only the thread that has the lock can call notifyAll
Restroom wait/notifyAll Analogy • The thread calling wait corresponds to the person who enters the restroom and finds there is no toilet paper • The person then leaves the restroom and waits outside • Other people may enter and leave, but the first person just waits • Eventually an attendant enters the restroom, refills the toilet paper, and shouts a notification • All the waiting people compete for the restroom
File BankAccountThreadTest.java Using synchronized methods 01: import java.util.Random; 02: 03: /** 04: This program runs four threads that deposit and withdraw 05: money from the same bank account. 06: */ 07: public class BankAccountThreadTest 08: { 09: public static void main(String[] args) 10: {
11: BankAccount account = new BankAccount(); 12: 13: DepositThread t0 = new DepositThread(account, 100); 14: WithdrawThread t1 = new WithdrawThread(account, 100); 15: DepositThread t2 = new DepositThread(account, 100); 16: WithdrawThread t3 = new WithdrawThread(account, 100); 17: 18: t0.start(); 19: t1.start(); 20: t2.start(); 21: t3.start(); 22: } 23: }
File BankAccount.java 01: /** 02: A bank account has a balance that can be changed by 03: deposits and withdrawals. 04: */ 05: public class BankAccount 06: { 07: /** 08: Constructs a bank account with a zero balance 09: */ 10: public BankAccount() 11: { 12: balance = 0; 13: } 14: 15: /** 16: Deposits money into the bank account. 17: @param amount the amount to deposit
18: */ 19: public synchronized void deposit(double amount) 20: { 21: System.out.print("Depositing " + amount); 22: double newBalance = balance + amount; 23: System.out.println(", new balance is " + newBalance); 24: balance = newBalance; 25: notifyAll(); 26: } 27: 28: /** 29: Withdraws money from the bank account. 30: @param amount the amount to withdraw 31: */ 32: public synchronized void withdraw(double amount) 33: throws InterruptedException 34: { 35: while (balance < amount) 36: wait(); 37: System.out.print("Withdrawing " + amount);
38: double newBalance = balance - amount; 39: System.out.println(", new balance is " + newBalance); 40: balance = newBalance; 41: } 42: 43: /** 44: Gets the current balance of the bank account. 45: @return the current balance 46: */ 47: public double getBalance() 48: { 49: return balance; 50: } 51: 52: private double balance; 53: }
Animation • Animation shows different objects moving or changing as time progresses • Thread programming is useful in animation • An algorithm animation helps visualize the steps in the algorithm
Algorithm Animation • Runs in a separate thread that periodically updates an image of the current state of the algorithm • It then pauses so the user can see the change • After a short time the algorithm thread wakes up and runs to the next point of interest • It updates the image again and pauses again