【JDK8】從 synchronized、Lock 到 StampedLock
之前在 StampedLock Idioms 中看到有介紹 JDK8 新的 首先來看個簡易的 package cc.openhome; import java.util.Arrays; public class ArrayList<E> { private Object[] elems; private int next; public ArrayList(int capacity) { elems = new Object[capacity]; } public ArrayList() { this(16); } public void add(E e) { if(next == elems.length) { elems = Arrays.copyOf(elems, elems.length * 2); } elems[next++] = e; } public E get(int index) { return (E) elems[index]; } public int size() { return next; } } 如果將這個 package cc.openhome; public class ArrayListDemo { public static void main(String[] args) { ArrayList list = new ArrayList(); new Thread(() -> { while (true) { list.add(1); } }).start(); new Thread(() -> { while (true) { list.add(2); } }).start(); } } 在這個範例中建立了兩個執行緒,分別於 Exception in thread "Thread-1" java.lang.ArrayIndexOutOfBoundsException: 64 at cc.openhome.ArrayList.add(ArrayList.java:21) at cc.openhome.ArrayListDemo.lambda$main$1(ArrayListDemo.java:15) at cc.openhome.ArrayListDemo$$Lambda$2/1072591677.run(Unknown Source) at java.lang.Thread.run(Thread.java:744) synchronized學過多執行緒的都知道,這是機率問題,有可能發生,也有可能沒發生,就先因陣列長度過長,JVM 分配到的記憶體不夠,而發生 package cc.openhome; import java.util.Arrays; public class SynchronizedArrayList<E> { private Object[] elems; private int next; public SynchronizedArrayList(int capacity) { elems = new Object[capacity]; } public SynchronizedArrayList() { this(16); } public synchronized void add(E e) { if(next == elems.length) { elems = Arrays.copyOf(elems, elems.length * 2); } elems[next++] = e; } public synchronized E get(int index) { return (E) elems[index]; } public synchronized int size() { return next; } } 這是學習 Java 多執行緒時一定會接觸到的基本概念,如果在方法上標示 ReentrantLock
package cc.openhome; import java.util.Arrays; import java.util.concurrent.locks.*; public class ReentrantLockArrayList<E> { private Lock lock = new ReentrantLock(); private Object[] elems; private int next; public ReentrantLockArrayList(int capacity) { elems = new Object[capacity]; } public ReentrantLockArrayList() { this(16); } public void add(E elem) { lock.lock(); try { if (next == elems.length) { elems = Arrays.copyOf(elems, elems.length * 2); } elems[next++] = elem; } finally { lock.unlock(); } } public E get(int index) { lock.lock(); try { return (E) elems[index]; } finally { lock.unlock(); } } public int size() { lock.lock(); try { return next; } finally { lock.unlock(); } } } 如果有兩個執行緒都想呼叫 ReentrantReadWriteLock
例如可使用 package cc.openhome; import java.util.Arrays; import java.util.concurrent.locks.*; public class ReentrantReadWriteLockArrayList<E> { private ReadWriteLock lock = new ReentrantReadWriteLock(); private Object[] elems; private int next; public ReentrantReadWriteLockArrayList(int capacity) { elems = new Object[capacity]; } public ReentrantReadWriteLockArrayList() { this(16); } public void add(E elem) { lock.writeLock().lock(); try { if (next == elems.length) { elems = Arrays.copyOf(elems, elems.length * 2); } elems[next++] = elem; } finally { lock.writeLock().unlock(); } } public E get(int index) { lock.readLock().lock(); try { return (E) elems[index]; } finally { lock.readLock().unlock(); } } public int size() { lock.readLock().lock(); try { return next; } finally { lock.readLock().unlock(); } } } 如此設計之後,若執行緒都多是在呼叫 StampedLock
然而,如果讀取執行緒很多,寫入執行緒甚少的情況下,使用 JDK8 新增了 假設之前的 package cc.openhome; import java.util.Arrays; import java.util.concurrent.locks.*; public class StampedLockArrayList<E> { private StampedLock lock = new StampedLock(); private Object[] elems; private int next; public StampedLockArrayList(int capacity) { elems = new Object[capacity]; } public StampedLockArrayList() { this(16); } public void add(E elem) { long stamp = lock.writeLock(); try { if (next == elems.length) { elems = Arrays.copyOf(elems, elems.length * 2); } elems[next++] = elem; } finally { lock.unlockWrite(stamp); } } public E get(int index) { long stamp = lock.tryOptimisticRead(); Object elem = elems[index]; if (!lock.validate(stamp)) { stamp = lock.readLock(); try { elem = elems[index]; } finally { lock.unlockRead(stamp); } } return (E) elem; } public int size() { long stamp = lock.tryOptimisticRead(); int size = next; if (!lock.validate(stamp)) { stamp = lock.readLock(); try { size = next; } finally { lock.unlockRead(stamp); } } return size; } } 範例中使用了 在範例 在 |