Concurrency: Performance Tuning

1. Comparing Mutex Technologies

  Lock的效率通常比synchronized高,但使用synchronized具有更好的可读性。如果加锁的代码比较耗时,耗费在加锁和解锁上的时间就会显得微不足道。

2. Lock-Free Containers

  Java SE5加入了新的线程安全的容器类,不使用互斥锁,提高了性能。其基本策略是,对容器的修改和读取可以同时进行:对容器的修改会先在单独的副本上完成,修改完成后,被修改的副本会替换掉原容器中的对应内容;从容器中进行读取时,只能读取到已经完成的修改。

  CopyOnWriteArrayList会在被修改时创建整个数组的副本,原数组还会被保留,提供安全的读取。修改完成后,原数组会被替换为修改后的数组(这个替换是一个原子操作),之后的读取就可以获得修改后的数据。CopyOnWriteArrayList不会在多个迭代器遍历和修改列表时抛出ConcurrentModificationException

  CopyOnWriteArraySet使用CopyOnWriteArrayList实现Lock-Free。

  ConcurrentHashMapConcurrentLinkedQueue也使用类似的方式来允许同时读写,但在写入时只会有部分内容被复制。

3. Optimistic Locking

  部分Atomic类允许乐观锁定(Optimistic Locking),在进行计算时不使用互斥锁,计算完成后,再通过compareAndSet()更新Atomic类。compareAndSet()需要同时提供一个旧值和一个新值,如果旧值和当前Atomic对象的值不同(其他任务也同时进行了修改),compareAndSet()会失败。通常我们使用互斥锁来防止多个任务同时修改同一个对象;但有时候我们会乐观的认为要修改的对象不会同时被其他任务修改,不使用锁定,从而获得性能上的提升。

4. ReadWriteLocks

  ReadWriteLock可以在没有写入的时候允许多个任务同时读取共享资源。当有任务进行写入时,写锁定被打开,此时所有任务都无法读取,直到写锁定释放。

  对于写入较少而有多个任务读取的场景,ReadWriteLock会产生一定的优化效果。

  一个例子如下:

import java.util.concurrent.*;
import java.util.concurrent.locks.*;
import java.util.*;
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)
System.out.println(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
}
System.out.println("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());
}
}
import java.util.concurrent.*; import java.util.concurrent.locks.*; import java.util.*; 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) System.out.println(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 } System.out.println("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()); } }
import java.util.concurrent.*;
import java.util.concurrent.locks.*;
import java.util.*;

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)
                System.out.println(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
            }
            System.out.println("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());
    }
}