JAVA编程思想笔记 : 并发 [ 三 ]

性能调优

 

lock 和  synchronized

//: concurrency/SynchronizationComparisons.java
package concurrency; /* Added by Eclipse.py */
// Comparing the performance of explicit Locks
// and Atomics versus the synchronized keyword.
import java.util.concurrent.*;
import java.util.concurrent.atomic.*;
import java.util.concurrent.locks.*;
import java.util.*;
import static net.mindview.util.Print.*;

abstract class Accumulator {
  public static long cycles = 50000L;
  // Number of Modifiers and Readers during each test:
  private static final int N = 4;
  public static ExecutorService exec =
    Executors.newFixedThreadPool(N*2);
  private static CyclicBarrier barrier =
    new CyclicBarrier(N*2 + 1);
  protected volatile int index = 0;
  protected volatile long value = 0;
  protected long duration = 0;
  protected String id = "error";
  protected final static int SIZE = 100000;
  protected static int[] preLoaded = new int[SIZE];
  static {
    // Load the array of random numbers:
    Random rand = new Random(47);
    for(int i = 0; i < SIZE; i++)
      preLoaded[i] = rand.nextInt();
  }
  public abstract void accumulate();
  public abstract long read();
  private class Modifier implements Runnable {
    public void run() {
      for(long i = 0; i < cycles; i++)
        accumulate();
      try {
        barrier.await();
      } catch(Exception e) {
        throw new RuntimeException(e);
      }
    }
  }
  private class Reader implements Runnable {
    private volatile long value;
    public void run() {
      for(long i = 0; i < cycles; i++)
        value = read();
      try {
        barrier.await();
      } catch(Exception e) {
        throw new RuntimeException(e);
      }
    }
  }
  public void timedTest() {
    long start = System.nanoTime();
    for(int i = 0; i < N; i++) {
      exec.execute(new Modifier());
      exec.execute(new Reader());
    }
    try {
      barrier.await();
    } catch(Exception e) {
      throw new RuntimeException(e);
    }
    duration = System.nanoTime() - start;
    printf("%-13s: %13d\n", id, duration);
  }
  public static void
  report(Accumulator acc1, Accumulator acc2) {
    printf("%-22s: %.2f\n", acc1.id + "/" + acc2.id,
      (double)acc1.duration/(double)acc2.duration);
  }
}

class BaseLine extends Accumulator {
  { id = "BaseLine"; }
  public void accumulate() {
    if( index < SIZE){
      value += preLoaded[index++];
      if(index >= SIZE) index = 0;
    }

  }
  public long read() { return value; }
}

class SynchronizedTest extends Accumulator {
  { id = "synchronized"; }
  public synchronized void accumulate() {
    value += preLoaded[index++];
    if(index >= SIZE) index = 0;
  }
  public synchronized long read() {
    return value;
  }
}

class LockTest extends Accumulator {
  { id = "Lock"; }
  private Lock lock = new ReentrantLock();
  public void accumulate() {
    lock.lock();
    try {
      value += preLoaded[index++];
      if(index >= SIZE) index = 0;
    } finally {
      lock.unlock();
    }
  }
  public long read() {
    lock.lock();
    try {
      return value;
    } finally {
      lock.unlock();
    }
  }
}

class AtomicTest extends Accumulator {
  { id = "Atomic"; }
  private AtomicInteger index = new AtomicInteger(0);
  private AtomicLong value = new AtomicLong(0);
  public void accumulate() {
    // Oops! Relying on more than one Atomic at
    // a time doesn't work. But it still gives us
    // a performance indicator:
    int i = index.getAndIncrement();

    if( index.get() < SIZE){

      value.getAndAdd(preLoaded[i]);
      if(++i >= SIZE)
        index.set(0);
    }

  }
  public long read() { return value.get(); }
}

public class SynchronizationComparisons {
  static BaseLine baseLine = new BaseLine();
  static SynchronizedTest synch = new SynchronizedTest();
  static LockTest lock = new LockTest();
  static AtomicTest atomic = new AtomicTest();
  static void test() {
    print("============================");
    printf("%-12s : %13d\n", "Cycles", Accumulator.cycles);
    baseLine.timedTest();
    synch.timedTest();
    lock.timedTest();
    atomic.timedTest();
    Accumulator.report(synch, baseLine);
    Accumulator.report(lock, baseLine);
    Accumulator.report(atomic, baseLine);
    Accumulator.report(synch, lock);
    Accumulator.report(synch, atomic);
    Accumulator.report(lock, atomic);
  }
  public static void main(String[] args) {
    int iterations = 5; // Default
    if(args.length > 0) // Optionally change iterations
      iterations = new Integer(args[0]);
    // The first time fills the thread pool:
    print("Warmup");
    baseLine.timedTest();
    // Now the initial test doesn't include the cost
    // of starting the threads for the first time.
    // Produce multiple data points:
    for(int i = 0; i < iterations; i++) {
      test();
      Accumulator.cycles *= 2;
    }
    Accumulator.exec.shutdown();
  }
} 

输出结果:

Connected to the target VM, address: '127.0.0.1:62371', transport: 'socket'
Warmup
BaseLine     :      21694209
============================
Cycles       :         50000
BaseLine     :       2180204
synchronized :      42437398
Lock         :      30372376
Atomic       :      26275467
synchronized/BaseLine : 19.46
Lock/BaseLine         : 13.93
Atomic/BaseLine       : 12.05
synchronized/Lock     : 1.40
synchronized/Atomic   : 1.62
Lock/Atomic           : 1.16
============================
Cycles       :        100000
BaseLine     :       5193179
synchronized :      82977464
Lock         :      31370928
Atomic       :      11210371
synchronized/BaseLine : 15.98
Lock/BaseLine         : 6.04
Atomic/BaseLine       : 2.16
synchronized/Lock     : 2.65
synchronized/Atomic   : 7.40
Lock/Atomic           : 2.80
============================
Cycles       :        200000
BaseLine     :       8938838
synchronized :     165124963
Lock         :      78268801
Atomic       :      13180858
synchronized/BaseLine : 18.47
Lock/BaseLine         : 8.76
Atomic/BaseLine       : 1.47
synchronized/Lock     : 2.11
synchronized/Atomic   : 12.53
Lock/Atomic           : 5.94
============================
Cycles       :        400000
BaseLine     :      24426392
synchronized :     331777960
Lock         :     115546684
Atomic       :      29905100
synchronized/BaseLine : 13.58
Lock/BaseLine         : 4.73
Atomic/BaseLine       : 1.22
synchronized/Lock     : 2.87
synchronized/Atomic   : 11.09
Lock/Atomic           : 3.86
============================
Cycles       :        800000
BaseLine     :      34345855
synchronized :     633854425
Lock         :     245733266
Atomic       :      87583065
synchronized/BaseLine : 18.46
Lock/BaseLine         : 7.15
Atomic/BaseLine       : 2.55
synchronized/Lock     : 2.58
synchronized/Atomic   : 7.24
Lock/Atomic           : 2.81
============================
Cycles       :       1600000
BaseLine     :      62631708
synchronized :    1594457825
Lock         :     654792500
Atomic       :     138332107
synchronized/BaseLine : 25.46
Lock/BaseLine         : 10.45
Atomic/BaseLine       : 2.21
synchronized/Lock     : 2.44
synchronized/Atomic   : 11.53
Lock/Atomic           : 4.73
============================
Cycles       :       3200000
BaseLine     :     128165186
synchronized :    3528972948
Lock         :    1278760479
Atomic       :     285986269
synchronized/BaseLine : 27.53
Lock/BaseLine         : 9.98
Atomic/BaseLine       : 2.23
synchronized/Lock     : 2.76
synchronized/Atomic   : 12.34
Lock/Atomic           : 4.47
============================
Cycles       :       6400000
BaseLine     :     220485251
synchronized :    6981991134
Lock         :    2650398226
Atomic       :     500422566
synchronized/BaseLine : 31.67
Lock/BaseLine         : 12.02
Atomic/BaseLine       : 2.27
synchronized/Lock     : 2.63
synchronized/Atomic   : 13.95
Lock/Atomic           : 5.30

 

整理成表格:

 

no Cycles BaseLine synchronized Lock Atomic
1 50000 2180204 42437398 30372376 26275467
2 100000 5193179 82977464 31370928 11210371
3 200000 8938838 165124963 78268801 13180858
4 400000 24426392 331777960 115546684 29905100
5 800000 34345855 633854425 245733266 87583065
6 1600000 62631708 1594457825 654792500 138332107
7 3200000 128165186 3528972948 1278760479 285986269
8 6400000 220485251 6981991134 2650398226 500422566

 

JAVA编程思想笔记 : 并发 [ 三 ]

 

使用 Lock 通常会比使用 synchronized 要高效

 

免锁容器

 

 CopyOnWriteArrayList是Java并发包中提供的一个并发容器,它是个线程安全且读操作无锁的ArrayList,写操作则通过创建底层数组的新副本来实现,是一种读写分离的并发策略,我们也可以称这种容器为"写时复制器",Java并发包中类似的容器还有CopyOnWriteSet。

 

优缺点分析

  了解了CopyOnWriteArrayList的实现原理,分析它的优缺点及使用场景就很容易了。

  优点:

  读操作性能很高,因为无需任何同步措施,比较适用于读多写少的并发场景。Java的list在遍历时,若中途有别的线程对list容器进行修改,则会抛出ConcurrentModificationException异常。而CopyOnWriteArrayList由于其"读写分离"的思想,遍历和修改操作分别作用在不同的list容器,所以在使用迭代器进行遍历时候,也就不会抛出ConcurrentModificationException异常了

  缺点:

  缺点也很明显,一是内存占用问题,毕竟每次执行写操作都要将原容器拷贝一份,数据量大时,对内存压力较大,可能会引起频繁GC;二是无法保证实时性,Vector对于读写操作均加锁同步,可以保证读和写的强一致性。而CopyOnWriteArrayList由于其实现策略的原因,写和读分别作用在新老不同容器上,在写操作执行过程中,读不会阻塞但读取到的却是老容器的数据。

 

CopyOnWriteArraySet 将使用CopyOnWriteArrayList来实现其免锁行为

 

ConcurrentHashMap和 ConcurrentLinkedQueue 使用了类似的技术,允许并发的读取和写入.但是容器中只有部分内容而不是整个容器可以被复制和修改.

ConcurrentHashMap不会抛出 ConcurrentModificationException.

 

乐观锁


Type                             Read time     Write time
Synched ArrayList 10r 0w        3975043910              0
Synched ArrayList 10r 0w        2766145687              0
Synched ArrayList 10r 0w        2324410921              0
Synched ArrayList 10r 0w        2352972749              0
Synched ArrayList 10r 0w        2511789031              0
Synched ArrayList 10r 0w        2987712606              0
Synched ArrayList 10r 0w        2602829236              0
Synched ArrayList 10r 0w        3502788085              0
Synched ArrayList 10r 0w        4335015877              0
Synched ArrayList 10r 0w        4047738828              0

CopyOnWriteArrayList 10r 0w      127857882              0
CopyOnWriteArrayList 10r 0w       62679837              0
CopyOnWriteArrayList 10r 0w       54443863              0
CopyOnWriteArrayList 10r 0w       56359211              0
CopyOnWriteArrayList 10r 0w       74350707              0
CopyOnWriteArrayList 10r 0w       62574412              0
CopyOnWriteArrayList 10r 0w       60151492              0
CopyOnWriteArrayList 10r 0w       68195080              0
CopyOnWriteArrayList 10r 0w       74290655              0
CopyOnWriteArrayList 10r 0w       62879388              0


预计相差 40 倍



Synched ArrayList 9r 1w         4351248439      519078520      
readTime + writeTime =          4870326959
Synched ArrayList 9r 1w         4580132832      528211025      
readTime + writeTime =          5108343857      
Synched ArrayList 9r 1w         4535477318      530243150      
readTime + writeTime =          5065720468
Synched ArrayList 9r 1w         3729192259      449021236      
readTime + writeTime =          4178213495
Synched ArrayList 9r 1w         4310570520      516373800      
readTime + writeTime =          4826944320      
Synched ArrayList 9r 1w         3727406116      444126621      
readTime + writeTime =          4171532737
Synched ArrayList 9r 1w         5088467838      581730237      
readTime + writeTime =          5670198075
Synched ArrayList 9r 1w         3871264858      467694613      
readTime + writeTime =          4338959471
Synched ArrayList 9r 1w         3907490866      475651205      
readTime + writeTime =          4383142071
Synched ArrayList 9r 1w         4857326451      572377421      
readTime + writeTime =          5429703872

CopyOnWriteArrayList 9r 1w        64259585       58038121
readTime + writeTime =           122297706
CopyOnWriteArrayList 9r 1w       103041865       55306617
readTime + writeTime =           158348482
CopyOnWriteArrayList 9r 1w        76859340       45666997
readTime + writeTime =           122526337
CopyOnWriteArrayList 9r 1w        82963384       49150571
readTime + writeTime =           132113955
CopyOnWriteArrayList 9r 1w        85530058       47494007
readTime + writeTime =           133024065
CopyOnWriteArrayList 9r 1w       156166751       61169655
readTime + writeTime =           217336406
CopyOnWriteArrayList 9r 1w       102234854       50495800
readTime + writeTime =           152730654
CopyOnWriteArrayList 9r 1w       118083485       59992123
readTime + writeTime =           178075608
CopyOnWriteArrayList 9r 1w       168668483       60294659
readTime + writeTime =           228963142
CopyOnWriteArrayList 9r 1w        83152493       52633779
readTime + writeTime =           135786272



预计相差 30 倍


Synched ArrayList 5r 5w         1859681520     3059611972
readTime + writeTime =          4919293492
Synched ArrayList 5r 5w         2339497789     3231858118
readTime + writeTime =          5571355907
Synched ArrayList 5r 5w         1759631869     2999467850
readTime + writeTime =          4759099719
Synched ArrayList 5r 5w         1872095383     3013864853
readTime + writeTime =          4885960236
Synched ArrayList 5r 5w         2991087849     3226872592
readTime + writeTime =          6217960441
Synched ArrayList 5r 5w         2434089947     3523933235
readTime + writeTime =          5958023182
Synched ArrayList 5r 5w         2536545986     3119941132
readTime + writeTime =          5656487118
Synched ArrayList 5r 5w         2571197431     3003374484
readTime + writeTime =          5574571915
Synched ArrayList 5r 5w         2222675410     2791449002
readTime + writeTime =          5014124412
Synched ArrayList 5r 5w         1583914850     2689629756
readTime + writeTime =          4273544606



CopyOnWriteArrayList 5r 5w       142259670     1206935112
readTime + writeTime =          1349194782
CopyOnWriteArrayList 5r 5w        49615047      995217184
readTime + writeTime =          1044832231
CopyOnWriteArrayList 5r 5w        31987043      977226939
readTime + writeTime =          1009213982
CopyOnWriteArrayList 5r 5w        66506805     1040415224
readTime + writeTime =          1106922029
CopyOnWriteArrayList 5r 5w        35032835      994549959
readTime + writeTime =          1029582794
CopyOnWriteArrayList 5r 5w        38461110     1002445848
readTime + writeTime =          1040906958
CopyOnWriteArrayList 5r 5w        50651685     1071667187
readTime + writeTime =          1122318872
CopyOnWriteArrayList 5r 5w        32006848     1001262532
readTime + writeTime =          1033269380
CopyOnWriteArrayList 5r 5w        36295220     1011740649
readTime + writeTime =          1048035869
CopyOnWriteArrayList 5r 5w        34283434     1050960270
readTime + writeTime =          1085243704

预计相差  4 倍 


 

 

CopyOnWriteArrayList性能 要比 SynchronizedArrayList 性能好一些. 当读的情况多的时候,会有几十倍的差距.

当读写数据量大小趋向于平均时, 性能还有将近四倍的差距.

 

比较各种 Map 实现

 

 

Type                             Read time     Write time
Synched HashMap 10r 0w          6242418056              0
Synched HashMap 10r 0w          4146402296              0
Synched HashMap 10r 0w          5575418578              0
Synched HashMap 10r 0w          3834073584              0
Synched HashMap 10r 0w          5194782005              0
Synched HashMap 10r 0w          5161536963              0
Synched HashMap 10r 0w          4444520893              0
Synched HashMap 10r 0w          3871868695              0
Synched HashMap 10r 0w          4408989207              0
Synched HashMap 10r 0w          8023118598              0

ConcurrentHashMap 10r 0w         344685546              0
ConcurrentHashMap 10r 0w         346848415              0
ConcurrentHashMap 10r 0w         318704071              0
ConcurrentHashMap 10r 0w         405478178              0
ConcurrentHashMap 10r 0w         207930162              0
ConcurrentHashMap 10r 0w         366067455              0
ConcurrentHashMap 10r 0w         226500276              0
ConcurrentHashMap 10r 0w         296423990              0
ConcurrentHashMap 10r 0w         320249183              0
ConcurrentHashMap 10r 0w         508154405              0


读取性能差 10 倍以上





Synched HashMap 9r 1w           6513091741      753915689
readTime + writeTime =          7267007430
Synched HashMap 9r 1w           6028976406      641421250
readTime + writeTime =          6670397656
Synched HashMap 9r 1w           5676651670      653679779
readTime + writeTime =          6330331449
Synched HashMap 9r 1w           5908498114      494457577
readTime + writeTime =          6402955691
Synched HashMap 9r 1w           7834818531      844974551
readTime + writeTime =          8679793082
Synched HashMap 9r 1w           5914246757      645557937
readTime + writeTime =          6559804694
Synched HashMap 9r 1w           7138638960      744661626
readTime + writeTime =          7883300586
Synched HashMap 9r 1w           6789718783      710857815
readTime + writeTime =          7500576598
Synched HashMap 9r 1w           5310600463      599121405
readTime + writeTime =          5909721868
Synched HashMap 9r 1w           5655007305      586903118
readTime + writeTime =          6241910423


ConcurrentHashMap 9r 1w          216104674       54419560
readTime + writeTime =           270524234
ConcurrentHashMap 9r 1w          294262252       40927173
readTime + writeTime =           335189425
ConcurrentHashMap 9r 1w          264142918       43273241
readTime + writeTime =           307416159
ConcurrentHashMap 9r 1w          273924527       48728635
readTime + writeTime =           322653162
ConcurrentHashMap 9r 1w          369514606       50730634
readTime + writeTime =           420245240
ConcurrentHashMap 9r 1w          219419103       38947284
readTime + writeTime =           258366387
ConcurrentHashMap 9r 1w          252190432       30225329
readTime + writeTime =           282415761
ConcurrentHashMap 9r 1w          228296180       25051090
readTime + writeTime =           253347270
ConcurrentHashMap 9r 1w          303395962       49612912
readTime + writeTime =           353008874
ConcurrentHashMap 9r 1w          171637818       35856054
readTime + writeTime =           207493872


预计相差 25 倍以上.




Synched HashMap 5r 5w           3787200263     3601518353
readTime + writeTime =          7388718616
Synched HashMap 5r 5w           3089274985     2733346333
readTime + writeTime =          5822621318
Synched HashMap 5r 5w           3408395059     3113962082
readTime + writeTime =          6522357141
Synched HashMap 5r 5w           3981206639     3679965007
readTime + writeTime =          7661171646
Synched HashMap 5r 5w           3265915194     3142623898
readTime + writeTime =          6408539092
Synched HashMap 5r 5w           3164752807     3000101498
readTime + writeTime =          6164854305
Synched HashMap 5r 5w           3336219924     3014104874
readTime + writeTime =          6350324798
Synched HashMap 5r 5w           3124944592     2991122381
readTime + writeTime =          6116066973
Synched HashMap 5r 5w           3977208930     3839928054
readTime + writeTime =          7817136984
Synched HashMap 5r 5w           3074772608     2874037633
readTime + writeTime =          5948810241



ConcurrentHashMap 5r 5w           99540221      462089771
readTime + writeTime =           561629992
ConcurrentHashMap 5r 5w          240567089      518136870
readTime + writeTime =           758703959
ConcurrentHashMap 5r 5w          155490877      432835809
readTime + writeTime =           588326686
ConcurrentHashMap 5r 5w          220516994      483831256
readTime + writeTime =           704348250
ConcurrentHashMap 5r 5w          216032229      572084619
readTime + writeTime =           788116848
ConcurrentHashMap 5r 5w          197179986      620970499
readTime + writeTime =           818150485
ConcurrentHashMap 5r 5w          254407618      544484450
readTime + writeTime =           798892068
ConcurrentHashMap 5r 5w          208138769      501331795
readTime + writeTime =           709470564
ConcurrentHashMap 5r 5w          180873142      492012085
readTime + writeTime =           672885227
ConcurrentHashMap 5r 5w          164806795      559711679
readTime + writeTime =           724518474

预计相差 10 倍以上

 

ConcurrentHashMap 完虐  synchronizedMap

 

乐观加锁

//: concurrency/FastSimulation.java
package concurrency; /* Added by Eclipse.py */
import java.util.concurrent.*;
import java.util.concurrent.atomic.*;
import java.util.*;
import static net.mindview.util.Print.*;

public class FastSimulation {
  static final int N_ELEMENTS = 100000;
  static final int N_GENES = 30;
  static final int N_EVOLVERS = 50;
  static final AtomicInteger[][] GRID =
    new AtomicInteger[N_ELEMENTS][N_GENES];
  static Random rand = new Random(47);
  static class Evolver implements Runnable {
    public void run() {
      while(!Thread.interrupted()) {
        // Randomly select an element to work on:
        int element = rand.nextInt(N_ELEMENTS);
        for(int i = 0; i < N_GENES; i++) {
          int previous = element - 1;
          if(previous < 0) previous = N_ELEMENTS - 1;
          int next = element + 1;
          if(next >= N_ELEMENTS) next = 0;
          int oldvalue = GRID[element][i].get();
          // Perform some kind of modeling calculation:
          int newvalue = oldvalue +
            GRID[previous][i].get() + GRID[next][i].get();
          newvalue /= 3; // Average the three values
          if(!GRID[element][i]
            .compareAndSet(oldvalue, newvalue)) {
            // Policy here to deal with failure. Here, we
            // just report it and ignore it; our model
            // will eventually deal with it.
            print("Old value changed from " + oldvalue);
          }
        }
      }
    }
  }
  public static void main(String[] args) throws Exception {
    ExecutorService exec = Executors.newCachedThreadPool();
    for(int i = 0; i < N_ELEMENTS; i++)
      for(int j = 0; j < N_GENES; j++)
        GRID[i][j] = new AtomicInteger(rand.nextInt(1000));
    for(int i = 0; i < N_EVOLVERS; i++)
      exec.execute(new Evolver());
    TimeUnit.SECONDS.sleep(5);
    exec.shutdownNow();
  }
} /* (Execute to see output) *///:~

 

ReadWriteLock

 

 

//: concurrency/ReaderWriterList.java
package concurrency; /* Added by Eclipse.py */
import java.util.concurrent.*;
import java.util.concurrent.locks.*;
import java.util.*;
import static net.mindview.util.Print.*;

public class ReaderWriterList<T> {
  private ArrayList<T> lockedList;
  // Make the ordering fair:
  private ReentrantReadWriteLock lock =
    new ReentrantReadWriteLock(true);
  public ReaderWriterList(int size, T initialValue) {
    lockedList = new ArrayList<T>(
      Collections.nCopies(size, initialValue));
  }
  public T set(int index, T element) {
    Lock wlock = lock.writeLock();
    wlock.lock();
    try {
      return lockedList.set(index, element);
    } finally {
      wlock.unlock();
    }
  }
  public T get(int index) {
    Lock rlock = lock.readLock();
    rlock.lock();
    try {
      // Show that multiple readers
      // may acquire the read lock:
      if(lock.getReadLockCount() > 1)
        print(lock.getReadLockCount());
      return lockedList.get(index);
    } finally {
      rlock.unlock();
    }
  }
  public static void main(String[] args) throws Exception {
    new ReaderWriterListTest(30, 1);
  }
}

class ReaderWriterListTest {
  ExecutorService exec = Executors.newCachedThreadPool();
  private final static int SIZE = 100;
  private static Random rand = new Random(47);
  private ReaderWriterList<Integer> list =
    new ReaderWriterList<Integer>(SIZE, 0);
  private class Writer implements Runnable {
    public void run() {
      try {
        for(int i = 0; i < 20; i++) { // 2 second test
          list.set(i, rand.nextInt());
          TimeUnit.MILLISECONDS.sleep(100);
        }
      } catch(InterruptedException e) {
        // Acceptable way to exit
      }
      print("Writer finished, shutting down");
      exec.shutdownNow();
    }
  }
  private class Reader implements Runnable {
    public void run() {
      try {
        while(!Thread.interrupted()) {
          for(int i = 0; i < SIZE; i++) {
            list.get(i);
            TimeUnit.MILLISECONDS.sleep(1);
          }
        }
      } catch(InterruptedException e) {
        // Acceptable way to exit
      }
    }
  }
  public ReaderWriterListTest(int readers, int writers) {
    for(int i = 0; i < readers; i++)
      exec.execute(new Reader());
    for(int i = 0; i < writers; i++)
      exec.execute(new Writer());
  }
} /* (Execute to see output) *///:~