Presentation is loading. Please wait.

Presentation is loading. Please wait.

1 Chapter 7: Process Synchronization 2 Contents Background The Critical-Section Problem Synchronization Hardware Semaphores Classical Problems of Synchronization.

Similar presentations


Presentation on theme: "1 Chapter 7: Process Synchronization 2 Contents Background The Critical-Section Problem Synchronization Hardware Semaphores Classical Problems of Synchronization."— Presentation transcript:

1

2 1 Chapter 7: Process Synchronization

3 2 Contents Background The Critical-Section Problem Synchronization Hardware Semaphores Classical Problems of Synchronization Monitors Java Synchronization Synchronization in Solaris 2 Synchronization in Windows NT

4 3 Background Concurrent access to shared data may result in data inconsistency. Maintaining data consistency requires mechanisms to ensure the orderly execution of cooperating processes. Shared-memory solution to bounded-butter problem (Chapter 4) has a race condition on the class data count § 7.1

5 4 Bounded Buffer public class BoundedBuffer { public void enter(Object item) { // producer calls this method } public Object remove() { // consumer calls this method } // potential race condition on count private volatile int count; }

6 5 enter() Method // producer calls this method public void enter(Object item) { while (count == BUFFER_SIZE) ; // do nothing // add an item to the buffer ++count; buffer[in] = item; in = (in + 1) % BUFFER_SIZE; }

7 6 remove() Method // consumer calls this method public Object remove() { Object item; while (count == 0) ; // do nothing // remove an item from the buffer --count; item = buffer[out]; out = (out + 1) % BUFFER_SIZE; return item; }

8 7 In machine language ++count register 1 = count ; register 1 = register 1 + 1; count = register 1 --count register 2 = count ; register 2 = register 2 – 1; count = register 2 The concurrent execution of the statements ++count and –count is equivalent to a sequential execution where the lower-level statements are interleaved in some arbitrary order.

9 8 Race Condition T 0 : producer execute rigister 1 := count {register 1 =5} T 1 : producer execute rigister 1 := rigister 1 +1 {register 1 =6} T 2 : consumer execute rigister 2 := count {register 2 =5} T 3 : consumer execute rigister 2 := rigister 2 –1 {register 2 =4} T 4 : producer execute count := rigister 1 { count =6} T 5 : consumer execute count := rigister 2 { count =4} To guard against the race condition, we need to ensure that only one thread at a time can be manipulating the variable count. To make such a guarantee, we require some form of synchronization of the threads. Should be 5. Error!

10 9 Critical-Section Problem Each thread has a segment of code, called a critical section, in which the thread may be changing common variables, updating a table, writing a file, and so on. When one thread is executing in its critical section, no other thread is to be allowed to execute in its critical section … mutually exclusive. The critical-section problem is how to choose a protocol that the threads can use to cooperate. § 7.2

11 10 Critical-Section Problem Each thread has a segment of code, called a critical section, in which the thread may be changing common variables, updating a table, writing a file, and so on. When one thread is executing in its critical section, no other thread is to be allowed to execute in its critical section … mutually exclusive. The critical-section problem is how to choose a protocol that the threads can use to cooperate. § 7.2 ( ) When one thread is executing in its critical section, no other thread is to be allowed to execute in its critical section, this is called _________. (A) global data (B) common method (C) mutually exclusive (D) constant accessing (E) value assertion Answer: C

12 11 Critical-Section Problem 1. Mutual Exclusion. If thread Ti is executing in its critical section, then no other threads can be executing in their critical sections. 2. Progress. If no thread is executing in its critical section and there exist some threads that wish to enter their critical sections, then only those threads that are not executing in their noncritical section can participate in the decision of which will enter its critical section next, and this selection cannot be postponed indefinitely. 3. Bounded Waiting. A bound must exist on the number of times that other threads are allowed to enter their critical sections after a process has made a request to enter its critical section and before that request is granted. This bound prevents starvation of any single thread.

13 12 Critical-Section Problem 1. Mutual Exclusion. If thread Ti is executing in its critical section, then no other threads can be executing in their critical sections. 2. Progress. If no thread is executing in its critical section and there exist some threads that wish to enter their critical sections, then only those threads that are not executing in their noncritical section can participate in the decision of which will enter its critical section next, and this selection cannot be postponed indefinitely. 3. Bounded Waiting. A bound must exist on the number of times that other threads are allowed to enter their critical sections after a process has made a request to enter its critical section and before that request is granted. This bound prevents starvation of any single thread. Short Answer Question: A solution to the critical-section problem must satisfy three requirements: mutual exclusive, progress, and bounded waiting. Please explain the meaning of these requirements.

14 13 Critical-Section Problem 1. Mutual Exclusion. If thread Ti is executing in its critical section, then no other threads can be executing in their critical sections. 2. Progress. If no thread is executing in its critical section and there exist some threads that wish to enter their critical sections, then only those threads that are not executing in their noncritical section can participate in the decision of which will enter its critical section next, and this selection cannot be postponed indefinitely. 3. Bounded Waiting. A bound must exist on the number of times that other threads are allowed to enter their critical sections after a process has made a request to enter its critical section and before that request is granted. This bound prevents starvation of any single thread. Multiple Choice Question: ( ) Which of the following is not one of the requirements for a solution of critical-section problem? (A) progress (B) loopless (C) mutual exclusive (D) bounded waiting Answer: B

15 14 Critical-Section Problem We can make no assumption concerning the relative speed of the n threads. The solutions should not rely on any assumptions concerning the hardware instructions. However, the basic machine-language instructions (the primitive instructions such as load, store, and test ) are executed atomically.

16 15 Critical-Section Problem We can make no assumption concerning the relative speed of the n threads. The solutions should not rely on any assumptions concerning the hardware instructions. However, the basic machine-language instructions (the primitive instructions such as load, store, and test ) are executed atomically. True-False Question: ( ) When solving critical section problems, we should make proper assumption concerning the relative speed of the threads. Answer: ×

17 16 Two-Tasks Solutions Each thread is implemented using the worker class. (Fig 7.1) (Fig 7.1) A call to the methods criticalSection() and nonCriticalSection () represents the places where each thread performs its critical and noncritical sections. Prior to calling its critical section, each thread will call the method enteringCriticalSection (). A thread will not return from enteringCriticalSection () until it is able to enter its critical section. § 7.3

18 17 Worker Thread public class Worker extends Thread { public Worker(String n, int i, MutualExclusion s) { name = n; id = i; shared = s; } public void run() { /* see next slide */ } private String name; private int id; private MutualExclusion shared; }

19 18 run() Method of Worker Thread public void run() { while (true) { shared.enteringCriticalSection(id); system.out.println(name + “ is in critical section ” ); MutualExclusion.criticalSection(); shared.leavingCriticalSection(id); System.out.println(name + “ is out of critical section ” ); MutualExclusion.noncriticalSection(); } Codes in critical section Codes out of critical section

20 19 MutualExclusion Abstract Class public abstract class MutualExclusion { public static void criticalSection() { // simulate the critical section } public static void nonCriticalSection() { // simulate the non-critical section } public abstract void enteringCriticalSection(int t); public abstract void leavingCriticalSection(int t); public static final int TURN_0 = 0; public static final int TURN_1 = 1; } This abstract class will serve as a template for the following three algorithms

21 20 Testing Each Algorithm public class TestAlgorithm { public static void main(String args[]) { MutualExclusion alg = new Algorithm_1(); Worker first = new Worker("Runner 0", 0, alg); Worker second = new Worker("Runner 1", 1, alg); first.start(); second.start(); } Creating the two threads and used to test each algorithm

22 21 Algorithm 1 public class Algorithm_1 extends MutualExclusion { public Algorithm_1() { turn = TURN_0; } public void enteringCriticalSection(int t) { while (turn != t) Thread.yield(); } public void leavingCriticalSection(int t) { turn = 1 - t; } private volatile int turn; } § 7.3.1 Keeps the thread in the Runnable state, but also allows the JVM to select another Runnable thread of equal priority to run.

23 22 Algorithm 1 Ensures that only one thread at a time can be in its critical section. However, does not satisfy the progress requirement, since it requires strict alternation of threads in the execution of their critical section.

24 23 Algorithm 2 public class Algorithm_2 extends MutualExclusion { public Algorithm_2() { flag[0] = false; flag[1] = false; } public void enteringCriticalSection(int t) { int other = 1 - t; flag[t] = true; while (flag[other] == true) Thread.yield(); } public void leavingCriticalSection(int t) { flag[t] = false; } private volatile boolean[] flag = new boolean[2]; }

25 24 Algorithm 2 public class Algorithm_2 extends MutualExclusion { public Algorithm_2() { flag[0] = false; flag[1] = false; } public void enteringCriticalSection(int t) { int other = 1 - t; flag[t] = true; while (flag[other] == true) Thread.yield(); } public void leavingCriticalSection(int t) { flag[t] = false; } private volatile boolean[] flag = new boolean[2]; } Short Answer Question: Examine the algorithm on the right for solving two-tasks critical-section problem. What this algorithm has achieved and what kind of problem this algorithm is encountered, if any ?

26 25 Algorithm 2 The mutual-exclusion requirement is satisfied. Unfortunately, the progress requirement is still not met. When both threads perform the while statement, they will loop forever as the value of flag for the other thread is true.

27 26 Algorithm 3 public class Algorithm_3 extends MutualExclusion { public Algorithm_3() { flag[0] = false; flag[1] = false; turn = TURN_0; } public void enteringCriticalSection(int t) {/* see next slides */ } public void leavingCriticalSection(int t) {{/* see next slides */ } private volatile int turn; private volatile boolean[] flag = new boolean[2]; }

28 27 Algorithm 3 public void enteringCriticalSection(int t) { int other = 1 - t; flag[t] = true; turn = other; while ( (flag[other] == true) && (turn == other) ) Thread.yield(); } public void leavingCriticalSection(int t) { flag[t] = false; }

29 28 Algorithm 3 Correct solution to the critical-section problem. If both threads try to enter at the same time, turn is set to both I and j at roughly the same time. Only one of these assignments lasts; the other will occur, but will be overwritten immediately. The eventual value of turn decides which of the two threads is allowed to enter its critical section first.

30 29 Synchronization Hardware Hardware instructions can be used effectively in solving critical-section problem. It allows as either to test and modify the content of a word, or to swap the contents of two words, atomically --- as one uninterruptible unit. § 7.4

31 30 Synchronization Hardware public class HardwareData { public HardwareData(boolean v) { data = v; } public boolean get() { return data; } public void set(boolean v) { data = v; } private boolean data; }

32 31 Test-and-Set Instruction (in Java) public class HardwareSolution { public static boolean testAndSet(HardwareData target) { HardwareData temp = new HardwareData(target.get()); target.set(true); return temp.get(); }

33 32 Test-and-Set Instruction (in Java) public class HardwareSolution { public static boolean testAndSet(HardwareData target) { HardwareData temp = new HardwareData(target.get()); target.set(true); return temp.get(); } True-False Question: ( ) The value of the lock returned by the test- and – set instruction is the value after the instruction is applied onto the lock. Answer: ×

34 33 Thread using Test-and-Set HardwareData lock = new HardwareData(false); while (true) { while (HardwareSolution.testAndSet(lock)) Thread.yield( ); // do nothing criticalSection( ); lock.set(false); nonCriticalSection( ); }

35 34 Thread using Test-and-Set HardwareData lock = new HardwareData(false); while (true) { while (HardwareSolution.testAndSet(lock)) Thread.yield( ); // do nothing criticalSection( ); lock.set(false); nonCriticalSection( ); } Multiple Choices Question: ( ) When calling a test-and-set instruction, a ______ must be provided as its parameter. (a) semaphore (b) integer (c) constant (d) lock Answer: d

36 35 Thread using Test-and-Set HardwareData lock = new HardwareData(false); while (true) { while (HardwareSolution.testAndSet(lock)) Thread.yield( ); // do nothing criticalSection( ); lock.set(false); nonCriticalSection( ); } True-False Question: ( ) When the value returned by the test-and-set instruction is false, it means that the locking is not successful. Answer: x

37 36 Swap instruction Also executed atomically. public static void swap(HardwareData a, HardwareData b) { HardwareData temp = new HardwareData(a.get()); a.set(b.get()); b.set(temp.get()); }

38 37 Thread using Swap HardwareData lock = new HardwareData(false); HardwareData key = new HardwareData(true); while (true) { key.set(true); do { HardwareSolution.swap(lock,key); } while (key.get() == true); criticalSection( ); lock.set(false); nonCriticalSection( ); }

39 38 Thread using Swap HardwareData lock = new HardwareData(false); HardwareData key = new HardwareData(true); while (true) { key.set(true); do { HardwareSolution.swap(lock,key); } while (key.get() == true); criticalSection( ); lock.set(false); nonCriticalSection( ); } Multiple Choices Question: ( ) The value of the key used to switch with the lock must be set to ______ before calling the swap method. (a) constant (b) false (c) true (d) integer Answer: c

40 39 Semaphore Previous solutions ( § 7.3) are not easy to generalize to more complex problems. Semaphores can be implemented as synchronization tool that does not require busy waiting. A semaphore S is an integer variable that can only be accessed via two indivisible (atomic) operations: P and V. P(S) { while S  0 ; // no-op; S--; } § 7.5 V(S) { S++; } These are classical definition of P and V, they require busy waiting.

41 40 Semaphore as General Synchronization Tool Semaphore S; // initialized to 1 P(S); CriticalSection() V(S); The value of a counting semaphore can range over an unrestricted domain. The value of a binary semaphore can range only between 0 and 1. § 7.5.1

42 41 Semaphore Eliminating Busy- Waiting The main disadvantage of previous solutions: they all require busy waiting…a problem in single CPU, multiprogramming system. Busy waiting wastes CPU cycles that some other process might be able to use productively. This type of semaphore is also called a spinlock. Spinlocks are useful in multiprocessor systems. The advantage of a spinlock is that no context switch is required when a process must wait on a lock (context switch may take considerable time). Spinlocks are useful when locks are expected to be held for short times.

43 42 Semaphore Eliminating Busy- Waiting The main disadvantage of previous solutions: they all require busy waiting…a problem in single CPU, multiprogramming system. Busy waiting wastes CPU cycles that some other process might be able to use productively. This type of semaphore is also called a spinlock. To overcome the need for busy waiting, the definition of P and V are modified.

44 43 Semaphore Eliminating Busy- Waiting The main disadvantage of previous solutions: they all require busy waiting…a problem in single CPU, multiprogramming system. Busy waiting wastes CPU cycles that some other process might be able to use productively. This type of semaphore is also called a spinlock. To overcome the need for busy waiting, the definition of P and V are modified. Short Answer Question: Although spinlocks waist CPU time with busy waiting, they are still useful in some systems. What are the advantage of spinlocks and in which situation they are considered useful?

45 44 Semaphore Eliminating Busy- Waiting P(S) { value--; if (value < 0) { add this process to list block; } V(S) { value++; if (value <= 0) { remove a process P from list wakeup(P); } The block operation places a process into a waiting queue associated with the semaphore, and the state of the process is switched to the waiting state. A process that is blocked, waiting on S, should be restarted when some other process execute a V operation. When the process is restarted by the wakeup operation, the process is changed from the waiting state to the ready state.

46 45 Synchronization Using Semaphores public class FirstSemaphore { public static void main(String args[]) { Semaphore sem = new Semaphore(1); Worker[] bees = new Worker[5]; for (int i = 0; i < 5; i++) bees[i] = new Worker(sem, "Worker " + (new Integer(i)).toString() ); for (int i = 0; i < 5; i++) bees[i].start(); }

47 46 Worker Thread public class Worker extends Thread { public Worker(Semaphore s, String n) { name = n; sem = s; } public void run() { while (true) { sem.P( ); // in critical section sem.V( ); // out of critical section } private Semaphore sem; private String name; }

48 47 Deadlock and Starvation Deadlock – two or more processes are waiting indefinitely for an event that can be caused by only one of the waiting processes. Let S and Q be two semaphores initialized to 1 P 0 P 1 P(S);P(Q); P(Q);P(S);  V(S);V(Q); V(Q)V(S); Starvation – indefinite blocking. A process may never be removed from the semaphore queue in which it is suspended. the execution of a V operation

49 48 Deadlock and Starvation Deadlock – two or more processes are waiting indefinitely for an event that can be caused by only one of the waiting processes. Let S and Q be two semaphores initialized to 1 P 0 P 1 P(S);P(Q); P(Q);P(S);  V(S);V(Q); V(Q)V(S); Starvation – indefinite blocking. A process may never be removed from the semaphore queue in which it is suspended. the execution of a V operation True-False Question: ( ) A deadlock can only happen in between two threads. Answer: ×

50 49 Classical Problems of Synchronization Bounded-Buffer Problem Readers and Writers Problem Dining-Philosophers Problem § 7.6

51 50 Bounded-Buffer Problem public class BoundedBuffer { public BoundedBuffer() { /* see next slides */ } public void enter() { /* see next slides */ } public Object remove() { /* see next slides */ } private static final int BUFFER_SIZE = 2; private Semaphore mutex; private Semaphore empty; private Semaphore full; private int in, out; private Object[] buffer; } § 7.6.1

52 51 Bounded Buffer Constructor public BoundedBuffer() { // buffer is initially empty count = 0; in = 0; out = 0; buffer = new Object[BUFFER_SIZE]; mutex = new Semaphore(1); empty = new Semaphore(BUFFER_SIZE); full = new Semaphore(0); }

53 52 enter() Method public void enter(Object item) { empty.P(); mutex.P(); // add an item to the buffer buffer[in] = item; in = (in + 1) % BUFFER_SIZE; mutex.V(); full.V(); }

54 53 enter() Method public void enter(Object item) { empty.P(); mutex.P(); // add an item to the buffer buffer[in] = item; in = (in + 1) % BUFFER_SIZE; mutex.V(); full.V(); } Multiple Choices Question: ( ) Consider following program for the enter() method of bounded-buffer problem. Which instruction is missing from the blank? (a) empty.V() (b) mutex.V() (c) full.P() (d) empty.P() public void enter(Object item) { empty.P(); mutex.P(); // add an item to the buffer buffer[in] = item; in = (in + 1) % BUFFER_SIZE; __________; full.V(); } Answer: b

55 54 remove() Method public Object remove() { full.P(); mutex.P(); // remove an item from the buffer Object item = buffer[out]; out = (out + 1) % BUFFER_SIZE; mutex.V(); empty.V(); return item; }

56 55 Readers-Writers Problem The readers-writers problem has several variations, all involving priorities. first: no reader will be kept waiting unless a writer has already obtained permission to use the shared database. § 7.6.2 No reader should wait for other readers to finish simply because a writer is waiting. Starvation: writers

57 56 Readers-Writers Problem second: once a writer is ready, that writer performs its write as soon as possible. If a writer is waiting to access the object, no new readers may start reading. Starvation: readers Here we present the first solution.

58 57 Readers-Writers Problem second: once a writer is ready, that writer performs its write as soon as possible. If a writer is waiting to access the object, no new readers may start reading. Starvation: readers Here we present the first solution. True-False Question: ( ) If a reader will be kept waiting only if a writer is currently working on the database, the starvation may happen on the reader. Answer: ×

59 58 Readers-Writers Problem: Reader public class Reader extends Thread { public Reader(Database db) { server = db; } public void run() { int c; while (true) { c = server.startRead(); // now reading the database c = server.endRead(); } private Databaseserver; }

60 59 Readers-Writers Problem: Writer public class Writer extends Thread { public Writer(Database db) { server = db; } public void run() { while (true) { server.startWrite(); // now writing the database server.endWrite(); } private Databaseserver; }

61 60 Readers-Writers Problem (cont) public class Database { public Database() { readerCount = 0; mutex = new Semaphore(1); db = new Semaphore(1); } public int startRead() { /* see next slides */ } public int endRead() { /* see next slides */ } public void startWrite() { /* see next slides */ } public void endWrite() { /* see next slides */ } private int readerCount; // number of active readers Semaphore mutex; // controls access to readerCount Semaphore db; // controls access to the database }

62 61 startRead() Method public int startRead() { mutex.P(); ++readerCount; // if I am the first reader tell all others // that the database is being read if (readerCount == 1) db.P(); mutex.V(); return readerCount; }

63 62 endRead() Method public int endRead() { mutex.P(); --readerCount; // if I am the last reader tell all others // that the database is no longer being read if (readerCount == 0) db.V(); mutex.V(); return readerCount; }

64 63 endRead() Method public int endRead() { mutex.P(); --readerCount; // if I am the last reader tell all others // that the database is no longer being read if (readerCount == 0) db.V(); mutex.V(); return readerCount; } True-False Question: ( ) Although there are many readers working on the database with the startRead() and endRead() method, the semaphore db will be acquired (P operation) and released (V operation) by the same reader. Answer: ×

65 64 Writer Methods public void startWrite() { db.P(); } public void endWrite() { db.V(); }

66 65 Writer Methods public void startWrite() { db.P(); } public void endWrite() { db.V(); } Short Answer Question: Please explain why the startWrite() method does not need to use the semaphore mutex as startRead() does.

67 66 Dining-Philosophers Problem A classic synchronization problem because it is an example of a large class of concurrency-control problems. § 7.6.3

68 67 Dining-Philosophers Problem Simple solution: represent each chopstick by a semaphore. Get the chopstick: P Release the chopstick: V Shared data: Semaphore chopStick[ ] = new Semaphore[5];

69 68 Dining-Philosophers Problem Philosopher i: while (true) { // get left chopstick chopStick[i].P(); // get right chopstick chopStick[(i + 1) % 5].P(); eating(); //return left chopstick chopStick[i].V(); // return right chopstick chopStick[(i + 1) % 5].V(); thinking(); } Deadlock when all five philosophers get her left chopstick at the same time

70 69 Dining-Philosophers Problem Philosopher i: while (true) { // get left chopstick chopStick[i].P(); // get right chopstick chopStick[(i + 1) % 5].P(); eating(); //return left chopstick chopStick[i].V(); // return right chopstick chopStick[(i + 1) % 5].V(); thinking(); } Deadlock when all five philosophers get her left chopstick at the same time Multiple Choices Question: ( ) In Dining-Philosophers Problem, if all philosophers get their right chopstick at the same time, what will happen? (a) starvation (b) deadlock (c) blocking (d) synchronization (e) mutual exclusioncc Answer: b

71 70 Dining-Philosophers Problem Preventing deadlock by placing restrictions: –Allow at most four philosophers to be sitting simultaneously at the table. –Allow a philosopher to pick up only if both chopsticks are available. –Use an asymmetric solution (for example, an odd philosopher picks up left then right, whereas an even philosopher picks up her right chopstick and then her left.) (Section 7.7 presents a solution to the dining-philosophers problem.)

72 71 Monitors Although semaphores provide a convenient and effective mechanism for process synchronization, their incorrect use can still result in timing errors that are difficult to detect. Examples: § 7.6.4 mutex.V(); criticalSection(); mutex.P(); Several processes may be executing in their critical section simultaneously. mutex.V(); criticalSection(); mutex.P(); Several processes may be executing in their critical section simultaneously. mutex.P(); criticalSection(); mutex.P(); A deadlock will occur. mutex.P(); criticalSection(); mutex.P(); A deadlock will occur. A process omits the mutex.P(), or the mutex.V(), or both Either mutual exclusion is violated or a deadlock will occur. A process omits the mutex.P(), or the mutex.V(), or both Either mutual exclusion is violated or a deadlock will occur.

73 72 Monitors Although semaphores provide a convenient and effective mechanism for process synchronization, their incorrect use can still result in timing errors that are difficult to detect. Examples: § 7.6.4 mutex.V(); criticalSection(); mutex.P(); Several processes may be executing in their critical section simultaneously. mutex.V(); criticalSection(); mutex.P(); Several processes may be executing in their critical section simultaneously. mutex.P(); criticalSection(); mutex.P(); A deadlock will occur. mutex.P(); criticalSection(); mutex.P(); A deadlock will occur. A process omits the mutex.P(), or the mutex.V(), or both Either mutual exclusion is violated or a deadlock will occur. A process omits the mutex.P(), or the mutex.V(), or both Either mutual exclusion is violated or a deadlock will occur. Multiple Choices Question: ( ) What kind of problem can happen if more than one thread work on a semaphore in the following sequence? mutex.V(); criticalSection(); mutex.P(); (a) starvation (b) deadlock (c) blocking (d) not synchronizing (e) violate mutual exclusion Answer: e

74 73 A monitor is a high-level abstraction that provides thread safety. A monitor presents a set of programmer-defined operations that are provided mutual exclusion within the monitor. Syntax of a monitor: monitor monitor-name { // variable declarations public entry p1(…) { … } public entry p2(…) { … } Monitors § 7.7

75 74 Condition Variables Encapsulation: limits access to the local variables by only the local procedures. The monitor construct prohibits concurrent access to all procedures defined within the monitor. Only one thread may be active within the monitor at a time. Synchronization is built into the monitor type, the programmer does not need to code it explicitly. Special operations wait and signal can be invoked on the variables of type condition. condition x, y; A thread that invokes x.wait is suspended until another thread invokes x.signal

76 75 Condition Variables Encapsulation: limits access to the local variables by only the local procedures. The monitor construct prohibits concurrent access to all procedures defined within the monitor. Only one thread may be active within the monitor at a time. Synchronization is built into the monitor type, the programmer does not need to code it explicitly. Special operations wait and signal can be invoked on the variables of type condition. condition x, y; A thread that invokes x.wait is suspended until another thread invokes x.signal True-False Question: ( ) Although there maybe several threads inside the monitor at the same time, there can only be one thread with the state of active at a time. Answer: ○

77 76 Monitor with condition variables

78 77 Solution to Dining Philosophers monitor diningPhilosophers { int[] state = new int[5]; static final int THINKING = 0; static final int HUNGRY = 1; static final int EATING = 2; condition[] self = new condition[5]; public diningPhilosophers { for (int i = 0; i < 5; i++) state[i] = THINKING; } public entry pickUp(int i) { /* see next slides */ } public entry putDown(int i) { /* see next slides */ } private test(int i) {/* see next slides */ } } With this, philosopher i can delay herself when she is hungry, but is unable to obtain the chopsticks she needs.

79 78 pickUp() Procedure public entry pickUp(int i) { state[i] = HUNGRY; test(i); if (state[i] != EATING) self[i].wait; } Each philosopher, before starting to eat, must invoke the operation pickup(). May result in the suspension of the philosopher thread.

80 79 test() Procedure private test(int i) { if ( (state[(i + 4) % 5] != EATING) && (state[i] == HUNGRY) && (state[(i + 1) % 5] != EATING) ) { state[i] = EATING; self[i].signal; } Release self[i] so that the thread can proceed.

81 80 putDown() Procedure public entry putDown(int i) { state[i] = THINKING; // test left and right neighbors test((i + 4) % 5); test((i + 1) % 5); } Ok, I have finished eating, now you guys go try it!

82 81 Solution to Dining Philosophers Sequence of invoke: dp.pickUp(i) eat(); dp.putDown(i) This is a deadlock free solution but starvation is still possible.

83 82 Java Synchronization An application that ensures data consistency even when being concurrently accessed by multiple threads is said to be thread safe. In Section 7.5.2, using P and V semaphore operation for busy waiting was introduced. An alternative is allowing a process to block itself. § 7.8 In Java, have a thread call the Thread.yield() method can accomplish such blocking.

84 83 Busy Waiting The yield() method makes more effective use of the CPU than busy waiting does. However, using either busy waiting or yielding could potentially lead to a deadlock. Example: (Recall Section 7.3.1) (page 21 in this.ppt file) JVM ensure higher priority thread in the Runnable state will run before lower priority one. If the producer has higher priority than consumer and the buffer is full: the producer enter the while loop and wait (or yield) to another Runnable thread with equal priority while waiting for count to be decremented to less than BUFFER_SIZE. But the consumer has lower priority than producer and will never be scheduled by the JVM to run … deadlocked. The producer is waiting for the consumer to free buffer space and the consumer is waiting to be scheduled by the JVM. We need a better solution. § 7.8.1.1

85 84 Race Condition Java prevent race condition by “lock ownership.” Every object has a lock associated with it. Calling a synchronized method requires “ owning ” the lock. If a calling thread does not own the lock (another thread already owns it), the calling thread is placed in the entry set for the object ’ s lock. The entry set represents the set of threads waiting for the lock to become available.

86 85 Race Condition Java prevent race condition by “lock ownership.” Every object has a lock associated with it. Calling a synchronized method requires “ owning ” the lock. If a calling thread does not own the lock (another thread already owns it), the calling thread is placed in the entry set for the object ’ s lock. The entry set represents the set of threads waiting for the lock to become available. Multiple Choices Question: ( ) In Java, if a calling thread cannot own a lock because another thread already owns it, the calling thread is placed in the _______ for the object’s lock and wait. (a) queue (b) wait array (c) monitor (d) entry set (e) blocking room Answer: d

87 86 Entry Set If the lock is available when a synchronized method is called, the calling thread becomes the owner of the object’s lock and it can enter the method. The lock is released when a thread exits the synchronized method. If the entry set for the lock is not empty when the lock is released, the JVM selects an arbitrary thread from this set as the new owner of the lock.

88 87 Synchronized enter() Method public synchronized void enter(Object item) { while (count == BUFFER_SIZE) Thread.yield(); ++count; buffer[in] = item; in = (in + 1) % BUFFER_SIZE; }

89 88 Synchronized remove() Method public synchronized Object remove() { Object item; while (count == 0) Thread.yield(); --count; item = buffer[out]; out = (out + 1) % BUFFER_SIZE; return item; }

90 89 Wait set synchronized method prevents race condition, however, the presence of yield() loop has led to another type of possible deadlock: When the buffer is full and the consumer is sleeping, the producer calls the enter() method will own the lock and perform yield(). And, when the consumer awakens and tries to call the remove(), it will be blocked. Wait set with wait() and notify() is introduced.

91 90 The wait() Method When a thread calls wait(), the following occurs: - the thread releases the object lock. - thread state is set to Blocked. - thread is placed in the wait set for the object.

92 91 Entry and Wait Sets

93 92 The notify() Method When a thread calls notify(), the following occurs: - Picks an arbitrary thread T from the wait set. - Moves T to the entry set. - Sets T from Blocked to Runnable. T can now compete for the object ’ s lock again.

94 93 enter() with wait/notify Methods public synchronized void enter(Object item) { while (count == BUFFER_SIZE) try { wait(); } catch (InterruptedException e) { } } ++count; buffer[in] = item; in = (in + 1) % BUFFER_SIZE; notify(); }

95 94 remove() with wait/notify Methods public synchronized Object remove() { Object item; while (count == 0) try { wait(); } catch (InterruptedException e) { } --count; item = buffer[out]; out = (out + 1) % BUFFER_SIZE; notify(); return item; }

96 95 The Readers-Writers Problem --Solved with Java Synchronization public class Database { public Database() { readerCount = 0; dbReading = false; dbWriting = false; } public synchronized int startRead() { /* see next slides */ } public synchronized int endRead() { /* see next slides */ } public synchronized void startWrite() { /* see next slides */ } public synchronized void endWrite() { /* see next slides */ } private int readerCount; private boolean dbReading; private boolean dbWriting; } § 7.8.3

97 96 startRead() Method public synchronized int startRead() { while (dbWriting == true) { try { wait(); } catch (InterruptedException e) { } ++readerCount; if (readerCount == 1) dbReading = true; return readerCount; } If I am the first reader tell all others that the database is being read

98 97 endRead() Method public synchronized int endRead() { --readerCount if (readerCount == 0) db.notifyAll(); return readerCount; } If I am the last reader tell all others that the database is no longer being read

99 98 Writer Methods public void startWrite() { while (dbReading == true || dbWriting == true) { try { wait(); } catch (InterruptedException e) { } } dbWriting = true; } public void endWrite() { dbWriting = false; notifyAll(); } Once there are either no readers or writers indicate that the database is being written

100 99 Multiple Notifications notify() selects an arbitrary thread from the wait set. *This may not be the thread that you want to be selected. Java does not allow you to specify the thread to be selected. notifyAll() removes ALL threads from the wait set and places them in the entry set. This allows the threads to decide among themselves who should proceed next. notifyAll() is a conservative strategy that works best when multiple threads may be in the wait set. § 7.8.4

101 100 Block Synchronization In Java, blocks of code – rather than entire methods – may be declared as synchronized. public void someMethod() { synchronized(this) { // remainder of the method } } This yields a lock scope that is typically smaller than a synchronized method. The amount of time between when a lock is assigned and when it is released is defined as the scope of the lock. § 7.8.5

102 101 Block Synchronization In Java, blocks of code – rather than entire methods – may be declared as synchronized. public void someMethod() { synchronized(this) { // remainder of the method } } This yields a lock scope that is typically smaller than a synchronized method. The amount of time between when a lock is assigned and when it is released is defined as the scope of the lock. § 7.8.5 Multiple Choices Question: ( )The amount of time between when a lock is assigned and when it is released is defined as the _______ of the lock. (a) length (b) entry point (c) scope (d) size (e) duration Answer: C

103 102 Block Synchronization In Java, blocks of code – rather than entire methods – may be declared as synchronized. public void someMethod() { synchronized(this) { // remainder of the method } } This yields a lock scope that is typically smaller than a synchronized method. The amount of time between when a lock is assigned and when it is released is defined as the scope of the lock. § 7.8.5 Multiple Choices Question: ( ) In Java, the scope of the lock can be defined within the size of a (a) method (b) procedure (c) block (d) message (e) entry set Answer: C

104 103 Block Synchronization (cont) Object mutexLock = new Object();... public void someMethod() { // non-critical section synchronized(mutexLock) { try { mutexLock.wait(); } catch (InterruptedException ie) {} } synchronized(mutexLock) { mutexLock.notify(): }

105 104 Java Semaphores Java does not provide a semaphore, but a basic semaphore can be constructed using Java synchronization mechanism. § 7.8.6

106 105 Semaphore Class public class Semaphore { public Semaphore() { value = 0; } public Semaphore(int v) { value = v; } public synchronized void P() { /* see next slide */ } public synchronized void V() { /* see next slide */ } private int value; }

107 106 P and V Operations public synchronized void P() { while (value <= 0) { try { wait(); } catch (InterruptedException e) { } } value --; } public synchronized void V() { ++value; notify(); }

108 107 Synchronization Rules A thread that owns the lock for an object can enter another synchronized method (or block) for the same object. A thread can nest synchronized method invocations for different objects. Thus a thread can simultaneously own the lock for several different objects. If a method is not declared as synchronized, then it can be invoked regardless of lock ownership, even while another synchronized method for the same object is executing. § 7.8.7

109 108 Synchronization Rules A thread that owns the lock for an object can enter another synchronized method (or block) for the same object. A thread can nest synchronized method invocations for different objects. Thus a thread can simultaneously own the lock for several different objects. If a method is not declared as synchronized, then it can be invoked regardless of lock ownership, even while another synchronized method for the same object is executing. § 7.8.7 True-False Question: ( ) Although a thread cannot own many locks at the same time, it can invoke many synchronized methods within a lock at the same time. Answer: ×

110 109 Java Monitors ( 略 ) § 7.8.8

111 110 OS Synchronization Synchronization in Solaris 2 Provides: - adaptive mutex - condition variables - semaphores - reader-writer locks § 7.9

112 111 Adaptive Mutex An adaptive mutex protects access to every critical data item. It starts as a standard semaphore implemented as a spinlock. If the data are locked (in use), the adaptive mutex does one of two things: –If the lock is held by a thread that is currently running on another CPU, the thread spins while waiting for the lock to become available. –If the thread holding the lock is not currently in run state, the thread blocks and go to sleep until the lock being released. The thread holding the data is likely to end soon. Put to sleep for avoiding the spinning when the lock will not be freed reasonably quickly.

113 112 Adaptive Mutex An adaptive mutex protects access to every critical data item. It starts as a standard semaphore implemented as a spinlock. If the data are locked (in use), the adaptive mutex does one of two things: –If the lock is held by a thread that is currently running on another CPU, the thread spins while waiting for the lock to become available. –If the thread holding the lock is not currently in run state, the thread blocks and go to sleep until the lock being released. Put to sleep for avoiding the spinning when the lcok will not be freed reasonably quickly. Multiple Choices Question: ( ) Different operations may be adopted by the adaptive mutex mechanism when a thread is requesting a locked data. The decision is made by the status of (a) located memories (b) relative CPU speed (c) the thread holding the lock (d) the type of monitor entries Answer: C

114 113 Adaptive Mutex Adaptive mutex only protect those data that are accessed by short code segments where a lock will be held for less than a few hundred instructions. For longer code segments, condition variables and semaphores are used. –If the desired lock is already held, the thread issues a wait and sleep. –The cost of putting a thread to sleep and waking it is less than the cost of wasting several hundred instructions waiting in a spinlock. If the code segment is longer than that, spin waiting will be exceedingly inefficient.

115 114 Readers-Writers Lock The readers-writers locks are used to protect data that are accessed frequently, but usually only in a read-only manner. In these circumstances, readers-writers locks are more efficient than semaphores. Expensive to implement, so again they are used on only long sections of code. THINK

116 115 Windows NT Synchronization Windows NT Provides: - mutex - critical sections - semaphores - event objects

117 116 The End


Download ppt "1 Chapter 7: Process Synchronization 2 Contents Background The Critical-Section Problem Synchronization Hardware Semaphores Classical Problems of Synchronization."

Similar presentations


Ads by Google