COMPSCI 230 Threading Week8 Figure 1 Thread status diagram [http://www.programcreek.com/2009/03/thread-status/] Synchronization Lock DeadLock Why do we need Synchronization in Java? If your code is executing in multi-threaded environment, you need synchronization for objects, which are shared among multiple threads, to avoid any corruption of state or any kind of unexpected behavior. Synchronization in Java will only be needed if shared object is mutable
Q1, Look at the AccountWithoutSync.java, the customer wants to create 100 threads, and using each thread to add one penny. After 100 threads, the balance should equal to 100. package Q1; public class AccountWithoutSync { private Account account = new Account(); private Thread[] thread = new Thread[100]; public static void main(string[] args) { AccountWithoutSync test = new AccountWithoutSync(); System.out.println("What is balance? " + test.account.getbalance()); public AccountWithoutSync(){ ThreadGroup g = new ThreadGroup("group"); boolean done = false; for(int i =0 ; i< 100; i++) { thread[i] = new Thread(g,new AddAPennyThread(), "t"); thread[i].start(); while (!done) if(g.activecount() == 0) done = true; // An inner class of task for adding a penny to the account class AddAPennyThread extends Thread { public void run() { account.deposit(1); class Account { private int balance = 0; public int getbalance() { return balance; public void deposit(int amount) { int newbalance = balance + amount; Thread.sleep(5); catch (InterruptedException ex) { // do nothing balance = newbalance;
Q1-a: When you run AccountWithoutSync.java, what s the balance? 3 Q1-b: what s the problem? The object is shared among multiple threads, which has been corrupted by different threads. Q1-c: How to fix this problem? Public synchronized void deposit(int amount) Cooperation Among Threads Use the wait(), notify(), and notifyall() methods to facilitate communication among threads. The wait(), notify(), and notifyall() methods must be called in a synchronized method or a synchronized block on the calling object of these methods. Otherwise, an IllegalMonitorStateException would occur. The wait() method lets the thread wait until some condition occurs. When it occurs, you can use the notify() or notifyall() methods to notify the waiting threads to resume normal
execution. The notifyall() method wakes up all waiting threads, while notify() picks up only one thread from a waiting queue. Thread 1 Thread 2 synchronized (anobject) { // Wait for the condition to become true while (!condition) anobject.wait(); // Do something when condition is true catch (InterruptedException ex) { ex.printstacktrace(); Resume synchronized (anobject) { // When condition becomes true anobject.notify(); or anobject.notifyall();...
Q2: Try to finish ThreadCooperation.java. Write a program that demonstrates thread cooperation. Suppose that you create and launch two threads, one deposits to an account, and the other withdraws from the same account. The second thread has to wait if the amount to be withdrawn is more than the current balance in the account. Whenever new fund is deposited to the account, the first thread notifies the second thread to resume. If the amount is still not enough for a withdrawal, the second thread has to continue to wait for more fund in the account. Assume the initial balance is 0 and the amount to deposit and to withdraw is randomly generated. Q2-a : Implement code in Deposit() function, if the current balance is more than the withdraw amount, then the current thread will notify another thread to withdraw the money public synchronized void deposit(int amount) { balance += amount; System.out.println("Deposit " + amount + "\t\t\t\t\t" + getbalance()); notifyall(); Q2-b : Implement code in Withdraw() function, if the current balance is less than the withdraw amount, then the current thread will wait another thread to deposit more money? public synchronized void withdraw(int amount) { while (balance < amount) wait(); catch (InterruptedException ex) { ex.printstacktrace(); balance -= amount; System.out.println("\t\t\tWithdraw " + amount + "\t\t" + getbalance());
Q3: Modify the IncreaseNumber method to accept a parameter of type int. Write a recursive algorithm that increases number by 1 on each iteration and prints it to the console then recalls IncreaseNumber with the parameter decreased by 1. The recursion should stop when the counter param == 0. Include the current thread ID in the output. public void IncreaseNumber(int counter){ if(counter ==0){ return; else{ number = number + 1; System.out.println("Thread ID " + Thread.currentThread().getId() + ": " + number); IncreaseNumber(counter - 1); Q3-B: We see conflict between the threads in accessing the variable number. Without using the synchronized keyword. Create a ReentrantLock object and place a lock over the critical section of IncreaseNumber. ReentrantLock lock1 = new ReentrantLock(); //in constructor. lock1.lock(); number = number + 1; System.out.println("Thread ID " + Thread.currentThread().getId() + ": " + number); IncreaseNumber(counter - 1); lock1.unlock(); Q3-C Where is the critical section that is protected by the ReentrantLock? Between the lock() and unlock() calls on the ReentrantLock object. Q3-D: Add a period of sleep to the basecase of the recursion for 10 seconds. if(counter ==0){ Thread.sleep(10000); catch (InterruptedException e) { // TODO Auto-generated catch block e.printstacktrace(); return; else{//.
Q3-E: Change the lock acquisition to timeout after 3 seconds. If it times out, print that thread ID that couldn t obtain the lock. //needs a try/catch block if(lock1.trylock(3l, TimeUnit.SECONDS)){ // critical code here else{ System.out.println("Lock could not be obtained - Timeout: " + Thread.currentThread().getId()); catch (InterruptedException e) { // TODO Auto-generated catch block e.printstacktrace(); Q3-F: Why can a thread continue to recursively call the same method even if it contains the critical section protected by a lock? It is reentrant which means it is allowed to re-acquire the same lock without issue.