System Programming Practical Session 7 Synchronization Liveness Guarded

  • Slides: 32
Download presentation
System Programming Practical Session 7 Synchronization, Liveness, Guarded Methods, and Thread Timing

System Programming Practical Session 7 Synchronization, Liveness, Guarded Methods, and Thread Timing

Synchronization A mechanism allowing safely accessing shared resources. A thread accessing a synchronized method

Synchronization A mechanism allowing safely accessing shared resources. A thread accessing a synchronized method locks the object. The object cannot be accessed by other threads while it is locked.

Synchronization 1. Class Even{ 2. private long n = 0; 3. public long next(){

Synchronization 1. Class Even{ 2. private long n = 0; 3. public long next(){ 4. n++; 5. n++; 6. return n; 7. 8. } }

Synchronization 1. Class Even{ 2. private long n = 0; 3. public synchronized long

Synchronization 1. Class Even{ 2. private long n = 0; 3. public synchronized long next(){ 4. n++; 5. n++; 6. return n; 7. 8. } }

Synchronizing a block 1. public int foo() { 2. // do something safe 3.

Synchronizing a block 1. public int foo() { 2. // do something safe 3. synchronized(this) { 4. // do something 5. return 9; 6. } 7. }

Solution to safety problem 1. 2. 3. 4. 5. 6. 7. 8. 9. 10.

Solution to safety problem 1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. Printer Example (from practical session 1) class Printer { Printer() {} /** * @param i line number * @param s the string to concatenate 40 times */ public synchronized void print. ALine(int i, String s) { System. out. print(i + ") "); for (int j = 0; j < 40; j++) { System. out. print(s); } 12. System. out. println(); 13. } 14. }

1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14.

1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. 17. 18. 19. 20. public class Good. Synchronization{ public static void main(String[] a) { Printer p = new Printer(); Thread t 1 = new Thread( new Simple. Asynchronous. Task("a", p) ); Thread t 2 = new Thread( new Simple. Asynchronous. Task("b", p) ); t 1. start(); // prints some lines of aaaa t 2. start(); // prints some lines of bbbb } } class Simple. Asynchronous. Task implements Runnable { Printer m_p; String m_name; public Simple. Asynchronous. Task(String name, Printer p) { m_p = p; m_name = name; } public void run() { for (int i = 0; i<50; i++) { m_p. print. ALine(i, m_name); } } }

Wrong solution #1 1. public class Bad. Synchronization 2 2. public static void main(String[]

Wrong solution #1 1. public class Bad. Synchronization 2 2. public static void main(String[] a) { 3. Printer p 1 = new Printer(); 4. Printer p 2 = new Printer(); 5. Thread t 1 = new Thread( new Simple. Asynchronous. Task("a", p 1) ); 6. Thread t 2 = new Thread( new Simple. Asynchronous. Task("b", p 2) ); 7. 8. t 1. start(); // prints some lines of aaaa 9. t 2. start(); // prints some lines of bbbb 10. } 11. } 12. 13. class Simple. Asynchronous. Task implements Runnable { 14. . . the same like Good. Synchronization. java 15. class Printer { 16. . . the same like Good. Synchronization. java 17. }

Wrong solution #2 (no printer object) 1. class Bad. Synchronization { 2. public static

Wrong solution #2 (no printer object) 1. class Bad. Synchronization { 2. public static void main(String[] a) { 3. Thread t 1 = new Thread( new Simple. Asynchronous. Task("a") ); 4. Thread t 2 = new Thread( new Simple. Asynchronous. Task("b") ); 5. t 1. start(); // prints some lines of aaaa 6. t 2. start(); // prints some lines of bbbb 7. } 8. } 9. 10. class Simple. Asynchronous. Task implements Runnable { 11. . . the same like Good. Synchronization. java 12. public synchronized void print. ALine(int i, String s) { 13. System. out. print(i + ") "); 14. for (int j = 0; j < 40; j++) System. out. print(s); 15. System. out. println(); 16. } 17. }

Liveness A concurrent application's ability to execute in a timely manner. Liveness problems: •

Liveness A concurrent application's ability to execute in a timely manner. Liveness problems: • Deadlock • Starvation • Livelock

Deadlock Thread 1 acquire Printer acquire Scanner use printer use scanner release Printer release

Deadlock Thread 1 acquire Printer acquire Scanner use printer use scanner release Printer release Scanner Thread 1 Thread 2 acquire Scanner acquire Printer use scanner use printer release Scanner release Printer Thread 2

1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14.

1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. 17. 18. 19. 20. 21. public class Deadlock { public static void main(String[] args) { final Object resource 1 = “Printer"; final Object resource 2 = “Scanner"; Thread t 1 = new Thread(new Runnable() { public void run() { synchronized(resource 1) { System. out. println("Thread 1: locked resource 1"); try {Thread. sleep(50); } catch (Interrupted. Exception e){} synchronized(resource 2) { System. out. println("Thread 1: locked resource 2"); }/* release resource 2 */ }/* release resource 1 */ } } ); Thread t 2 = new Thread(new Runnable() { public void run() { synchronized(resource 2) { System. out. println("Thread 2: locked resource 2"); try{Thread. sleep(50); } catch(Interrupted. Exception e){} synchronized(resource 1) { System. out. println("Thread 2: locked resource 1"); }/* release resource 1 */ }/* release resource 2 */ } } ); t 1. start(); t 2. start(); } }

Resource Ordering All threads acquire the locks in the same order. Thread 1 acquire

Resource Ordering All threads acquire the locks in the same order. Thread 1 acquire Printer acquire Scanner use printer use scanner release Printer release Scanner Thread 2 acquire Printer acquire Scanner use printer use scanner release Printer release Scanner

Starvation Some threads are waiting forever for resources that are used by other threads.

Starvation Some threads are waiting forever for resources that are used by other threads.

Starvation Some threads are waiting forever for resources that are used by other threads.

Starvation Some threads are waiting forever for resources that are used by other threads. Example 1: A solution to the dinning philosophers problem in which philosophers 1, 3, 5 never eat, and philosophers 2, 4 eat whenever they want. 3 2 4 1 5

Starvation Example 2: Threads with priority. Each thread has a priority: Low or High.

Starvation Example 2: Threads with priority. Each thread has a priority: Low or High. Low priority threads execute only if there are no high priority threads. High priority threads keep coming. High Low

Livelock Threads are unable to make progress although they are not blocked.

Livelock Threads are unable to make progress although they are not blocked.

Livelock Threads are unable to make progress although they are not blocked. Example: two

Livelock Threads are unable to make progress although they are not blocked. Example: two threads trying to pass a shared corridor. Thread protocol: if there is an obstacle, then move aside.

Livelock Threads are unable to make progress although they are not blocked. Example: two

Livelock Threads are unable to make progress although they are not blocked. Example: two threads trying to pass a shared corridor. Thread protocol: if there is an obstacle, then move aside.

Guarded Methods The guarded method model delays the execution of a thread until a

Guarded Methods The guarded method model delays the execution of a thread until a condition is satisfied. A thread that is unable to proceed, waits for condition change made by another thread.

Basic Thread Timing Example Each thread is assigned a number from {1, 2, …,

Basic Thread Timing Example Each thread is assigned a number from {1, 2, …, k}. In any point in time, only threads assigned a certain number run. Other threads wait. 1 3 1 1

Basic Thread Timing Example Each thread is assigned a number from {1, 2, …,

Basic Thread Timing Example Each thread is assigned a number from {1, 2, …, k}. In any point in time, only threads assigned a certain number run. Other threads wait. 1 3 3 1

1. class Checker { 2. private int m_num; 3. public Checker(int num) { 4.

1. class Checker { 2. private int m_num; 3. public Checker(int num) { 4. this. m_num = num; 5. } 6. public synchronized void change(int num) { 7. this. m_num = num; 8. } 9. public synchronized boolean check(int num){ 10. return (num == this. m_num); 11. } 12. }

1. public class Threads 01 { 2. 3. public static void main(String[] args) {

1. public class Threads 01 { 2. 3. public static void main(String[] args) { Checker checker. Object = new Checker(0); 4. Thread t[] = new Thread[9]; 5. for (int i = 0; i < t. length ; i++) 6. 7. 8. 9. 10. t[i] = new Thread(new Sleep. Thread(i/3+1, "NO. " +(i+1)+ " done, was waiting for "+(i/3+1), checker. Object)); for (int i = 0; i < t. length; i++) t[i]. start(); try { Thread. sleep(1000); 11. checker. Object. change(1); 12. Thread. sleep(1000); 13. checker. Object. change(3); 14. Thread. sleep(1000); 15. checker. Object. change(2); 16. 17. } 18. } } catch (Interrupted. Exception e) {}

Solution #1: Busy Waiting 1. 2. 3. 4. 5. class Sleep. Thread implements Runnable

Solution #1: Busy Waiting 1. 2. 3. 4. 5. class Sleep. Thread implements Runnable { private int m_num; private String m_str. To. Print. When. Done; private Checker m_checker. Object; Sleep. Thread(int num, String str. To. Print. When. Done, Checker checker. Object) { 6. this. m_num = num; 7. this. m_str. To. Print. When. Done = str. To. Print. When. Done; 8. this. m_checker. Object = checker. Object; 9. } 10. public void run() { 11. /* busy waiting */ 12. while (!this. m_checker. Object. check(this. m_num)) 13. ; 14. System. out. println(this. m_str. To. Print. When. Done); 15. } 16. }

Solution #2: Sleep and Check 1. 2. 3. 4. 5. class Sleep. Thread implements

Solution #2: Sleep and Check 1. 2. 3. 4. 5. class Sleep. Thread implements Runnable { private int m_num; private String m_str. To. Print. When. Done; private Checker m_checker. Object; Sleep. Thread(int num, String str. To. Print. When. Done, Checker checker. Object) { 6. this. m_num = num; 7. this. m_str. To. Print. When. Done = str. To. Print. When. Done; 8. this. m_checker. Object = checker. Object; 9. } 10. public void run() { 11. while (!this. m_checker. Object. check(this. m_num)) 12. { 13. try { 14. Thread. sleep(100); 15. } catch (Interrupted. Exception e) {} 16. } 17. System. out. println(this. m_str. To. Print. When. Done); 18. } 19. }

1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14.

1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. 17. 18. 19. 20. 21. Solution #3: Wait and Notify class Sleep. Thread implements Runnable { ……… public void run() { this. m_checker. Object. return. When. Check. Is. True(m_num); System. out. println(m_str. To. Print. When. Done); } } class Checker { private int m_num; public Checker(int num) { this. m_num = num; } public synchronized void change(int num){ this. m_num = num; this. notify. All(); } public synchronized void return. When. Check. Is. True(int num) { while (num != m_num) { try { this. wait(); } catch (Interrupted. Exception e) {} } } }

Tips • Use a while loop (and not an if condition) to check the

Tips • Use a while loop (and not an if condition) to check the precondition • notify() Vs. notify. All() - which of them should you use? • When calling wait(), notify. All() or notify() on an object, make sure the calling thread holds the object's lock. Notice that if you do not hold the object's lock you will receive an illegal monitor runtime exception. • After performing a wait call on the object, the thread releases the object's lock. Furthermore, before exiting the wait set of the object, the thread must re-lock the object • A thread that releases an object's, lock will NOT release other locks it has

Advanced Synchronization Tools Count. Down. Latch Constructor: • Count. Down. Latch(int value) Methods: •

Advanced Synchronization Tools Count. Down. Latch Constructor: • Count. Down. Latch(int value) Methods: • void count. Down() Decrements the latch value by 1. • void await() Causes the current thread to wait until the latch has counted down to zero.

1. import java. util. concurrent. *; 2. public class Threads { 3. public static

1. import java. util. concurrent. *; 2. public class Threads { 3. public static void main(String[] args) 4. { 5. Count. Down. Latch latch. Object = new Count. Down. Latch (3); 6. 7. Server s = new Server (latch. Object); 8. Client c 1 = new Client (1, latch. Object); 9. Client c 2 = new Client (2, latch. Object); 10. Client c 3 = new Client (3, latch. Object); 11. Thread t 1=new Thread(s); 12. Thread t 2=new Thread(c 1); 13. Thread t 3=new Thread(c 2); 14. Thread t 4=new Thread(c 3); 15. t 1. start(); 16. t 2. start(); 17. t 3. start(); 18. t 4. start(); 19. } 20. }

1. class Server implements Runnable { 2. private Count. Down. Latch m_latch. Object; 3.

1. class Server implements Runnable { 2. private Count. Down. Latch m_latch. Object; 3. public Server(Count. Down. Latch latch. Object) 4. { 5. m_latch. Object = latch. Object; 6. } 7. public void run () 8. { 9. synchronized (System. out) 10. { 11. System. out. println("Server initialized"); 12. } 13. try { 14. m_latch. Object. await(); 15. } catch (Interrupted. Exception e) 16. { 17. return; 18. } 19. System. out. println("Server finished"); 20. } 21. }

1. class Client implements Runnable { 2. private int m_id; private Count. Down. Latch

1. class Client implements Runnable { 2. private int m_id; private Count. Down. Latch m_latch. Object; 3. public Client(int id, Count. Down. Latch latch. Object){ 4. m_id = id; 5. m_latch. Object = latch. Object; 6. } 7. public void run () { 8. synchronized (System. out) { 9. System. out. println("Client #" + m_id + " started"); 10. } 11. try { Thread. sleep(200); } catch (Interrupted. Exception e) {} 12. synchronized (System. out){ 13. System. out. println("Client #" + m_id + " doing something"); 14. } 15. try { Thread. sleep(200); } catch (Interrupted. Exception e) {} 16. synchronized (System. out){ 17. System. out. println("Client #" + m_id + " finished"); 18. } 19. m_latch. Object. count. Down(); 20. } 21. }