import java.util.concurrent.Semaphore; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; class TwoGateCounter { final int PEOPLE; Gates which; // Two separate workers will count the number of entrants // at the east and west gates final Thread west; final Thread east; // Count how many people entered overall. final FancyInteger counter; // Here we start the program public static void main(String[] args) { System.out.println("Starting"); TwoGateCounter c = new TwoGateCounter(Gates.MIX2,10000); long startTime = System.currentTimeMillis(); c.count(); long stopTime = System.currentTimeMillis(); long elapsedTime = stopTime-startTime; // Print the final result of `counter` System.out.println("counter = " + c.value()); System.out.println("elapsed time = " + elapsedTime); System.exit(1); } public TwoGateCounter(Gates w,int num) { PEOPLE = num; which = w; counter = new FancyInteger(0); Runnable r = null; switch(which) { case GATEE: // 1 east = new GateE(); west = new GateE(); break; case GATEI: // 2 east = new Thread(r=new GateI()); west = new Thread(r); break; case GATEELOCK: // 3 case GATEILOCK: east = new GateELock(); west = new GateELock(); break; case GATEESYNC: // 4 east = new GateESync(); west = new GateESync(); break; case GATEISYNC: // 5 east = new Thread(new GateISync()); west = new Thread(new GateISync()); break; case GATEEACK: // 8 east = new GateEAcq(); west = new GateEAcq(); break; case GATEIACK: // Skip east = new Thread(new GateIAcq()); west = new Thread(new GateIAcq()); break; case GATEESEM: // 9 case GATEISEM: east = new Thread(new GateISem()); west = new Thread(new GateISem()); break; case MIX1: // 6 east = new Thread(new GateISync()); west = new Thread(new GateESync()); break; case MIX2: // 7 //east = new GateELock(); //west = new GateESync(); //break; default: east = new Thread(new GateIAckWait()); west = new Thread(new GateIAckWait()); } } public void count() { // Not checking if east and west are null try { System.out.println("About to start"); east.start(); west.start(); //east.interrupt(); //west.interrupt(); east.join(); west.join(); } catch (InterruptedException e) { // Thread methods such as `start()` or `join()` may through `InterruptedException` System.out.println(e); } } public int value() { return counter.value(); } // Class gate which models people crossing a gate class GateE extends Thread { // The `run()` method is executed when calling `start()` public void run() { for (int i = 0; i < PEOPLE; i++) { counter.inc(); } } } class GateI implements Runnable { @Override public void run() { for (int i=0; i< PEOPLE; i++) { counter.inc(); } } } class GateELock extends Thread { public void run() { for (int i = 0; i < PEOPLE; i++) { counter.lock(); counter.inc(); counter.unlock(); } } } class GateESync extends Thread { // The `run()` method is executed when calling `start()` public void run() { for (int i = 0; i < PEOPLE; i++) { synchronized (counter) { counter.inc(); } } } } class GateISync implements Runnable { public void run() { for (int i=0; i< PEOPLE; i++) { counter.inc_sync(); } } } class GateEAcq extends Thread { public void run() { for (int i = 0; i < PEOPLE; i++) { try { counter.acquire(); counter.inc(); counter.release(); } catch (InterruptedException e) { System.out.println(e); } } } } class GateIAcq implements Runnable { public void run() { for (int i = 0; i < PEOPLE; i++) { try { // Acquire throws an InterruptedException if // the tread is interrupted so you have to either // 1. Stop what you are doing and throw it further // (probably best thing to do) // 2. Take corrective action that will undo the // interruption counter.sem.acquire(); counter.inc(); counter.sem.release(); } catch (InterruptedException e) { // In this case, an interruption would mean // that a person was actually not counted. // Here I reduce i to allow this to be counted again // in the next round of the loop. // But that's not very nice. --i; } } } } class GateIAckWait implements Runnable { public void run() { for (int i=0; i< PEOPLE; ++i) { System.out.println("Person: "+i ); while (true) { if (!counter.sem.tryAcquire()) { // do whatever I need to do // if I can't get to the critical section try { // The wait and the notify+release have to // be synchronized on the semaphore. // This is the monitor design pattern synchronized(counter.sem) { // Wait throws InterruptedException if the // thread is interrupted. // Again, you should probably stop what you // are doing and stop the thread (as it was interrupted). //System.out.println("going to wait"); counter.sem.wait(); //System.out.println("stopped waiting"); // Notice that while waiting, the lock of // counter.sem is NOT held. // When the thread is coming out from the wait // it will have to reacquire the lock of counter.se // in order to get back into the synchronized region // and continue with whatever code appears there // after the wait() } } catch (InterruptedException e) { // Here I don't do anything // If the thread is interrupted I still // want it to continue counting. // It will just go through the loop again and try to // acquire again System.out.println(e.toString()); } } else { counter.inc(); synchronized (counter.sem) { counter.sem.notify(); counter.sem.release(); } break; } } } } } class GateESem extends Thread { public void run() { for (int i = 0; i < PEOPLE; i++) { counter.inc_sem(); } } } class GateISem implements Runnable { @Override public void run() { for (int i=0; i< PEOPLE; i++) { counter.inc_sem(); } } } class FancyInteger { private int value; private void allocate() { this.sem = new Semaphore(1); this.lock = new ReentrantLock(); } public FancyInteger(int v) { value = v; allocate(); } public void inc() { ++value; } public Semaphore sem; private Lock lock; public void inc_lock() { lock.lock(); ++value; lock.unlock(); } public synchronized void inc_sync() { ++value; } public void inc_sync1() { // some code synchronized(this) { ++value; } // some code } public void inc_sem() { try { sem.acquire(); ++value; sem.release(); } catch (InterruptedException e) { System.out.println(e); } } public void acquire() throws InterruptedException { sem.acquire(); } public void release() { sem.release(); } public void lock() { lock.lock(); } public void unlock() { lock.unlock(); } public int value() { return value; } } public enum Gates { GATEE, GATEEACK, GATEESEM, GATEESYNC, GATEELOCK, GATEI, GATEIACK, GATEISEM, GATEISYNC, GATEILOCK, MIX1, MIX2 } }