Concurrency: Performance Tuning
Author: nex3z
2016-07-12
1. Comparing Mutex Technologies
Lock
的效率通常比synchronized
高,但使用synchronized
具有更好的可读性。如果加锁的代码比较耗时,耗费在加锁和解锁上的时间就会显得微不足道。
2. Lock-Free Containers
Java SE5加入了新的线程安全的容器类,不使用互斥锁,提高了性能。其基本策略是,对容器的修改和读取可以同时进行:对容器的修改会先在单独的副本上完成,修改完成后,被修改的副本会替换掉原容器中的对应内容;从容器中进行读取时,只能读取到已经完成的修改。
CopyOnWriteArrayList
会在被修改时创建整个数组的副本,原数组还会被保留,提供安全的读取。修改完成后,原数组会被替换为修改后的数组(这个替换是一个原子操作),之后的读取就可以获得修改后的数据。CopyOnWriteArrayList
不会在多个迭代器遍历和修改列表时抛出ConcurrentModificationException
。
CopyOnWriteArraySet
使用CopyOnWriteArrayList
实现Lock-Free。
ConcurrentHashMap
和ConcurrentLinkedQueue
也使用类似的方式来允许同时读写,但在写入时只会有部分内容被复制。
3. Optimistic Locking
部分Atomic
类允许乐观锁定(Optimistic Locking),在进行计算时不使用互斥锁,计算完成后,再通过compareAndSet()
更新Atomic
类。compareAndSet()
需要同时提供一个旧值和一个新值,如果旧值和当前Atomic
对象的值不同(其他任务也同时进行了修改),compareAndSet()
会失败。通常我们使用互斥锁来防止多个任务同时修改同一个对象;但有时候我们会乐观的认为要修改的对象不会同时被其他任务修改,不使用锁定,从而获得性能上的提升。
4. ReadWriteLocks
ReadWriteLock
可以在没有写入的时候允许多个任务同时读取共享资源。当有任务进行写入时,写锁定被打开,此时所有任务都无法读取,直到写锁定释放。
对于写入较少而有多个任务读取的场景,ReadWriteLock
会产生一定的优化效果。
一个例子如下:
import java.util.concurrent.*;
import java.util.concurrent.locks.*;
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();
return lockedList.set(index, element);
public T get(int index) {
Lock rlock = lock.readLock();
// Show that multiple readers
// may acquire the read lock:
if(lock.getReadLockCount() > 1)
System.out.println(lock.getReadLockCount());
return lockedList.get(index);
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 {
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");
private class Reader implements Runnable {
while(!Thread.interrupted()) {
for(int i = 0; i < SIZE; 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());
}
}