Critical Section Problems

 

  Here I wish to show you some of my DIY demo programs demonstrating how Critical Section Problems can be solved by using Peterson's Algorithm, Spinlocks as well as Semaphores.

 

  This is a demo program of Peterson's Algorithm. Here two clients send messages to their corresponding server threads, and the two server threads print messages on the same terminal without interference.

  1 import java.util.concurrent.atomic.*;
  2 import java.util.*;
  3 import java.io.*;
  4 
  5 class Server {
  6     /**
  7      *  This class simulates a Server System that can
  8      *         service two Client Threads simultaneously
  9      */
 10     private class Task extends Thread {
 11         /**
 12          *  This class simulates a Server Thread
 13          */
 14         private int id, cnt;
 15         private Scanner in;
 16         
 17         public Task(int id,PipedOutputStream pout) throws IOException {
 18             this.id = id;
 19             in = new Scanner(new PipedInputStream(pout));
 20         }
 21         public void run() {
 22             /* Print the messages from the client */
 23             try {
 24                while (!getAndPrintMsg());
 25            } catch (Exception e) {
 26                 System.err.println("Error 2: "+e);
 27             }
 28         }
 29         private boolean getAndPrintMsg() throws IOException {
 30             /* 
 31              * Implementation of Peterson's Algorithm:
 32              */
 33             String str = in.nextLine();
 34             if (!str.equals("over")) {
 35                 flag[id].set(true);
 36                 turn.set(1-id);
 37                 while (flag[1-id].get()&&turn.get()==1-id);
 38                 System.out.println("\t"+this+" #"+(++cnt)+": \t"+str);    // critical section
 39                 flag[id].set(false);
 40                 return false;
 41             } else {
 42                 return true;
 43             }
 44         }
 45         public String toString() {
 46             return "Server " + (id+1);
 47         }
 48     }
 49     
 50     private AtomicBoolean[] flag;
 51     private AtomicInteger turn;
 52     
 53     public Server(PipedOutputStream pout1,PipedOutputStream pout2) {
 54         // initialization of flag and turn
 55         flag = new AtomicBoolean[2];
 56         for (int i=0;i<2;i++) {
 57             flag[i] = new AtomicBoolean(false);
 58         }
 59         turn = new AtomicInteger(0);
 60         try {
 61             (new Task(0,pout1)).start();
 62             (new Task(1,pout2)).start();
 63         } catch (IOException e) {
 64             e.printStackTrace();
 65         }
 66     }
 67 }
 68 
 69 class Client extends Thread {
 70     /**
 71      *  This Class simulates a Client Thread
 72      */
 73     private int id;
 74     private PipedOutputStream pout;
 75     
 76     public Client(int id,PipedOutputStream pout) {
 77         this.id = id;
 78         this.pout = pout;
 79         start();
 80     }
 81     public void run() {
 82         try {
 83             for (int i=0;i<100;i++) {
 84                 String str = this+" obtains the console\n";
 85                 pout.write(str.getBytes());
 86             }
 87             pout.write("over\n".getBytes());
 88         } catch (Exception e) {
 89             System.err.println("Error 1: "+e);
 90         }
 91     }
 92     public String toString() {
 93         return "client "+(id+1);
 94     }
 95 }
 96 
 97 public class Main {
 98     
 99     public static void main(String[] args) {
100         PipedOutputStream pout1 = new PipedOutputStream();
101         PipedOutputStream pout2 = new PipedOutputStream();
102         try {
103             new Server(pout1, pout2);
104             new Client(0,pout1);
105             new Client(1,pout2);
106         } catch (Exception e) {
107             System.err.println("Error 0: "+e);
108         }
109     }
110 }

 

  The following program implements Spinlock by using Java AtomicBoolean class:

 1 import java.util.concurrent.atomic.*;
 2 
 3 public class Main {
 4     /**
 5      *  This class implements Spinlock Algorithm
 6      */
 7     private static  class Task extends Thread {
 8         /**
 9          *  This class implements a task thread
10          */
11         private int idx;
12         
13         public Task(int idx) {
14             this.idx = idx;
15             start();
16         }
17         public void run() {
18             LOOP:
19             for (int i=1;i<=3;i++) {
20                 while (wait[idx].get()&&lock.getAndSet(true));
21                 System.out.println("\t"+this+"  ("+i+")\tin Critical Section");
22                 int next = idx;
23                 for (int j=1;j<NUM;j++) {
24                     next = (next+1)%NUM;
25                     if (wait[next].get()) {
26                         wait[next].set(false);
27                         continue LOOP;
28                     }
29                 }
30                 lock.set(false);
31             }
32         }
33         public String toString() {
34             return "Task "+(idx+1);
35         }
36     }
37     
38     private static final int NUM = 50;
39     private static AtomicBoolean lock;
40     private static AtomicBoolean[] wait;
41     
42     public static void main(String[] args) {
43         lock = new AtomicBoolean(false);
44         wait = new AtomicBoolean[NUM];
45         for (int i=0;i<NUM;i++) {
46             wait[i] = new AtomicBoolean(false);
47         }
48         Task[] task = new Task[NUM];
49         for (int i=0;i<NUM;i++) {
50             task[i] = new Task(i);
51         }
52         for (int i=0;i<NUM;i++) {
53             try {
54                 task[i].join();
55             } catch (InterruptedException e) {
56                 e.printStackTrace();
57             }
58         }
59     }
60 }

 

  As the first demo of Java Semaphores, the following program solves the classical Sleeping Barber Problem.

  1 import java.util.concurrent.atomic.*;
  2 import java.util.concurrent.*;
  3 import java.util.*;
  4 
  5 public class Main {
  6     /**
  7      * This class simulates Sleeping Barber Problem
  8      */
  9     private static class Barber extends Thread{
 10         /**
 11          *  This class simulates a barber
 12          */
 13         
 14         public Barber() {
 15             start();
 16         }
 17         public void run() {
 18            try {
 19               while(num.get()>0) {
 20                    task.acquire();
 21                    service.release();
 22                    TimeUnit.MILLISECONDS.sleep(servTime);
 23                }
 24            } catch (Exception e) {
 25                e.printStackTrace();
 26            }
 27         }
 28     }
 29     private static class Customer extends Thread{
 30         /**
 31          *  This class simulates a customer
 32          */
 33         private int idx;
 34         
 35         public Customer(int idx) {
 36             this.idx = idx;
 37             start();
 38         }
 39         public void run() {
 40             try {
 41                 mutex.acquire();
 42                 if (chair>0) {
 43                     chair--;
 44                     mutex.release();
 45                 } else {
 46                     synchronized(this) {
 47                         System.out.println("\t"+this+" leaves");
 48                     }
 49                     mutex.release();
 50                     num.getAndDecrement();
 51                     return;
 52                 }
 53                 synchronized(this) {
 54                     System.out.println("\t"+this+" sits and waits");
 55                 }
 56                 task.release();
 57                 service.acquire();
 58                 num.getAndDecrement();
 59                 synchronized(this) {
 60                     System.out.println("\t"+this+" is served");
 61                 }
 62                 TimeUnit.MILLISECONDS.sleep(servTime);
 63                 mutex.acquire();
 64                 chair++;
 65                 mutex.release();
 66             } catch (InterruptedException e) {
 67                 e.printStackTrace();
 68             }
 69         }
 70         public String toString() {
 71             return "Customer  # "+idx+"\t";
 72         }
 73     }
 74     
 75     private static Semaphore mutex;
 76     private static int chair = 10;
 77     private static AtomicInteger num;
 78     private static Semaphore task;
 79     private static Semaphore service;
 80     private static int servTime = 25;
 81     private static int custTime = 20;
 82     
 83     public static void main(String[] argds) {
 84         mutex = new Semaphore(1);
 85         task = new Semaphore(0);
 86         service = new Semaphore(0);
 87         num = new AtomicInteger(50);
 88         System.out.println("Number of Chairs:\t"+chair);
 89         System.out.println("Number of Customers:\t"+num);
 90         System.out.println();
 91         int numVal = num.get();
 92         Barber barber = new Barber();
 93         Customer[] cust = new Customer[numVal];
 94         try {
 95              for (int i=0;i<numVal;i++) {
 96                  cust[i] = new Customer(i);
 97                  int t = (new Random()).nextInt(custTime);
 98                  TimeUnit.MILLISECONDS.sleep(t);
 99              }
100              barber.join();
101              for (int i=0;i<numVal;i++) {
102                  cust[i].join();
103              }
104         } catch (Exception e) {
105             e.printStackTrace();
106         }
107     }
108 }

 

  The second example simulates Smoking Lounge Problem, where a smoker cannot smoke when a non-smoker stays in the lounge, and a non-smoker will not enter the lounge as long as there is a smoker smoking in the lounge.

  1 import java.util.concurrent.atomic.*;
  2 import java.util.concurrent.*;
  3 import java.util.*;
  4 
  5 public class Main {
  6     /**
  7      *  This class simulates Lounge Problem
  8      */
  9     private static class Smoker extends Thread{
 10         /**
 11          *  This class simulates a smoker
 12          */
 13         private int idx;
 14         
 15         public Smoker(int idx) {
 16             this.idx = idx;
 17             start();
 18         }
 19         public void run() {
 20             try {
 21                 System.out.println("\t"+this+" comes in");
 22                 wait.acquire();
 23                 if (smkCnt.getAndIncrement()==0) {
 24                     semp.acquire();
 25                 }
 26                 wait.release();
 27                 System.out.println("\t"+this+" smokes");
 28                 int t = (new Random()).nextInt(10);
 29                 TimeUnit.MILLISECONDS.sleep(t);
 30                 System.out.println("\t"+this+" goes out");
 31                 if (smkCnt.decrementAndGet()==0) {
 32                     semp.release();
 33                 }
 34             } catch (Exception e) {
 35                 e.printStackTrace();
 36             }
 37         }
 38         public String toString() {
 39             return "Smoker # "+idx+" ";
 40         }
 41     }
 42     private static class Nonsmk extends Thread {
 43         /**
 44          * This class simulates a non-smoker
 45          */
 46         private int idx;
 47         
 48         public Nonsmk(int idx) {
 49             this.idx = idx;
 50             start();
 51         }
 52         public void run() {
 53             try {
 54                 wait.acquire();
 55                 if (nonCnt.getAndIncrement()==0) {
 56                     semp.acquire();
 57                 }
 58                 wait.release();
 59                 System.out.println("\t"+this+" comes in");
 60                 int t = (new Random()).nextInt(10);
 61                 TimeUnit.MILLISECONDS.sleep(t);
 62                 System.out.println("\t"+this+" goes out");
 63                 if (nonCnt.decrementAndGet()==0) {
 64                     semp.release();
 65                 }
 66             } catch (Exception e) {
 67                 e.printStackTrace();
 68             }
 69         }
 70         public String toString() {
 71             return "NonSmk  # "+idx+" ";
 72         }
 73     }
 74     
 75     private static final int num = 100;
 76     private static Semaphore wait;
 77     private static Semaphore semp;
 78     private static AtomicInteger smkCnt;
 79     private static AtomicInteger nonCnt;
 80     
 81     static {
 82         wait = new Semaphore(1);
 83         semp = new Semaphore(1);
 84         smkCnt = new AtomicInteger(0);
 85         nonCnt = new AtomicInteger(0);
 86     }
 87     public static void main(String[] args) {
 88         int smkNum=0, nonNum=0;
 89         Thread[] thread = new Thread[num];
 90         try {
 91             for (int i=0;i<num;i++) {
 92                 int r = (new Random()).nextInt(5);
 93                 if (r<3) {
 94                     thread[i] = new Nonsmk(nonNum++);
 95                 } else {
 96                     thread[i] = new Smoker(smkNum++);
 97                 }
 98                 int t = (new Random()).nextInt(5);
 99                 TimeUnit.MILLISECONDS.sleep(t);
100             }
101             for (int i=0;i<num;i++) {
102                 thread[i].join();
103             }
104         } catch (Exception e) {
105             e.printStackTrace();
106         }
107     }
108 }

 

  At the end of this article, I wish to generalize the smoking lounge problem above: given num different types of threads, as well as all pairs of i and j such that thread type i and type j cannot get access to the critical section together (possibly i==j), describe the behavior of each type of thread so that all of them can work orderly without deadlock or starvation.

  The input of the program should comprise: (1) one integer num indicating the number of thread types; (2) num integers indicating the number of each type of threads; (3) one integer N indicating the number of following input pairs; (4) N integer pairs representing the thread types that cannot coexist in the critical section.

  As a matter of fact, this problem is a generalization of many classical problems such as Reader-Writer Problem, Baboon Crossing Problem, and Search/Insert/Delete Problem. For example, we assume there are 100 threads to search, 50 threads to insert and 20 threads to delete, then the input should be:

100
20
0 2
1
2
2

  The following program is a naive general solution that I have come up with:

  1 import java.util.concurrent.atomic.*;
  2 import java.util.concurrent.*;
  3 import java.util.*;
  4 import java.io.*;
  5 
  6 class Input {
  7     private Scanner in;
  8     private StringTokenizer tok;
  9     
 10     public Input() {
 11         in = new Scanner(new BufferedInputStream(System.in));
 12     }
 13     public String nextString() {
 14         while  (tok==null||!tok.hasMoreTokens()) {
 15             tok = new StringTokenizer(in.nextLine());
 16         } 
 17         return tok.nextToken();
 18     }
 19     public int nextInt() {
 20         while  (tok==null||!tok.hasMoreTokens()) {
 21             tok = new StringTokenizer(in.nextLine());
 22         } 
 23         return Integer.parseInt(tok.nextToken());
 24     }
 25     public double nextDouble() {
 26         while  (tok==null||!tok.hasMoreTokens()) {
 27             tok = new StringTokenizer(in.nextLine());
 28         } 
 29         return Double.parseDouble(tok.nextToken());
 30     }
 31     public void close() {
 32         in.close();
 33     }
 34 }
 35 
 36 class CSP {
 37     private class Task extends Thread {
 38         private int type;
 39         
 40         public Task(int type) {
 41             this.type = type;
 42         }
 43         public void run() {
 44             try {
 45                 sempAcquire();
 46                 System.out.println("\t"+this+" is working.");
 47                 sempRelease();
 48             } catch (Exception e) {
 49                 System.err.println("TASK RUN Error: "+e);
 50             }
 51         }
 52         private void sempAcquire() throws InterruptedException {
 53             wait.acquire();
 54             if (mutex[type][type]) {
 55                 semp[type][type].acquire();
 56             }
 57             if (cnt[type].getAndIncrement()==0){
 58                 for (int i=0;i<type;i++) {
 59                     if (mutex[type][i]) {
 60                         semp[type][i].acquire();
 61                     }
 62                 }
 63                 for (int i=type+1;i<num;i++) {
 64                     if (mutex[i][type]) {
 65                         semp[i][type].acquire();
 66                     }
 67                 }
 68             }
 69             wait.release();
 70         }
 71         private void sempRelease() {
 72             if (cnt[type].getAndDecrement()==1){
 73                 for (int i=num-1;i>type;i--) {
 74                     if (mutex[i][type]) {
 75                         semp[i][type].release();
 76                     }
 77                 }
 78                 for (int i=type-1;i>=0;i--) {
 79                     if (mutex[type][i]) {
 80                         semp[type][i].release();
 81                     }
 82                 }
 83             }
 84             if (mutex[type][type]) {
 85                 semp[type][type].release();
 86             }
 87         }
 88         public String toString() {
 89             int idxVal = idx[type].getAndIncrement();
 90             return "Task "+type+" #"+idxVal;
 91         }
 92     }
 93     
 94     private int num;
 95     private Task[][] taskArr;
 96     private AtomicInteger[] cnt;
 97     private AtomicInteger[] idx;
 98     private Semaphore wait;
 99     private Semaphore[][] semp;
100     private boolean mutex[][];
101     
102     public CSP(int num) {
103         this.num = num;
104         taskArr = new Task[num][];
105         cnt = new AtomicInteger[num];
106         idx = new AtomicInteger[num];
107         for (int i=0;i<num;i++) {
108             cnt[i] = new AtomicInteger(0);
109             idx[i] = new AtomicInteger(0);
110         }
111         try {
112             wait = new Semaphore(1);
113             semp = new Semaphore[num][];
114             mutex = new boolean[num][];
115             for (int i=0;i<num;i++) {
116                 semp[i] = new Semaphore[i+1];
117                 mutex[i] = new boolean[i+1];
118             }
119         } catch (Exception e) {
120             System.err.println("CSP INIT Error: "+e);
121         }
122     }
123     public void genThread(int type,int n) {
124         taskArr[type] = new Task[n];
125         for (int i=0;i<n;i++) {
126             taskArr[type][i] = new Task(type);
127         }
128     }
129     public void setMutex(int i,int j) {
130         if (j>i) {
131             i += j;
132             j = i-j;
133             i -= j;
134         }
135         mutex[i][j] = true;
136         try {
137             semp[i][j] = new Semaphore(1);
138         } catch (Exception e) {
139             System.err.println("CSP SETMUTEX Error: "+e);
140         }
141     }
142     public void start() {
143         class Pair {
144             public int type,idx;
145             public Pair(int i,int j) {
146                 type = i;
147                 idx = j;
148             }
149         }
150         List<Pair> q = new LinkedList<Pair>();
151         for (int i=0;i<num;i++) {
152             for (int j=0;j<taskArr[i].length;j++) {
153                 q.add(new Pair(i,j));
154             }
155         }
156         Collections.shuffle(q,new Random());
157         while (!q.isEmpty()) {
158             Pair item = q.remove(0);
159             taskArr[item.type][item.idx].start();
160         }
161     }
162     public void join() {
163         try {
164             for (int i=0;i<num;i++) {
165                 for (int j=0;j<taskArr[i].length;j++) {
166                     taskArr[i][j].join();
167                 }
168             }
169         } catch (Exception e) {
170             System.err.println("CSP JOIN Error: "+e);
171         }
172     }
173 }
174 
175 
176 public class Main {
177     
178     public static void main(String[] args) {
179         Input in = new Input();
180         int n = in.nextInt();
181         CSP csp = new CSP(n);
182         for (int i=0;i<n;i++) {
183             csp.genThread(i,in.nextInt());
184         }
185         int m = in.nextInt();
186         for (int i=0;i<m;i++) {
187             csp.setMutex(in.nextInt(),in.nextInt());
188         }
189         in.close();
190         System.out.println("Critical Section Problem:");
191         csp.start();
192         csp.join();
193     }
194 }

 

 

References:

  1. Nutt, Gary. Operating Systems: A Modern Perspective[M].北京:机械工业出版社, 2004-02

  2. Eckel, Bruce. Thinking in Java[M]. 北京:机械工业出版社, 2007-06

 

posted on 2015-03-21 22:12  DevinZ  阅读(437)  评论(0编辑  收藏  举报

导航