显示锁:
Lock与ReentrantLock:
- Lock接口
void lock(); //获取锁
void lockInterruptibly() throws InterruptedException; //获取锁,且当前线程可被中断
boolean tryLock(); //尝试获取锁,true获取到锁, false未获取到锁
boolean tryLock(long time, TimeUnit unit) throws InterruptedException;
void unlock(); //释放锁
Condition newCondition(); //在当前锁上创建一个等待条件
- Lock的标准用法
Lock lock = new ReentrantLock();
lock.lock();
try{
// to do sth.
} finally{
lock.unlock(); //须要在finally中释放锁
}
轮询锁与定时锁:
- 轮询锁和定时锁可由tryLock来实现。
- 轮询锁,定时锁可以避免死锁的发生。
- 由tryLock实现轮询锁
public boolean transferMoney(Account fromAcct,
Account toAcct,
int amount,
long timeout,
TimeUnit unit){
long fixedDelay = getFixedDelayComponentNanos(timeout, unit);
long randMod = getRandomDelayModulusNanos(timeout, unit);
long stopTime = System.nanoTime() + unit.toNanos(timeout);
while (true){
if (fromAcct.lock.tryLock()){ //若获取到源账户锁
try{
if (toAcct.lock.tryLock()){ //若获取到目的账户锁
try{
if (fromAcct.getBalance() < amount){
throw new RuntimeException("money.not.enough");
} else{
fromAcct.debit(amount);
toAcct.credit(amount);
return true;
}
} finally{
toAcct.lock.unlock();
}
}
} finally{
fromAcct.lock.unlock();
}
}
if (System.nanoTime() < stopTime){
return false;
}
try {
Thread.sleep(fixedDelay + rand.nextLong()%randMod);
} catch (InterruptedException e) {
}
}
}
你也可以采用定时实现:
lock.tryLock(timeout, unit);
可中断的锁获取操作:
lock.lockInterruptibly();
try{
// maybe throws InterruptedException
doSomething();
} finally{
lock.unlock();
}
非块结构的加锁:
- 如ConcurrentHashMap中的分段锁实现。
性能考虑因素:
- 上图是对HashMap的测试,可见在jdk1.5时ReentrantLock比内置锁吞吐量高,jdk1.6差异就很小了。
- 性能是一个不断变化的指标,如果在昨天的测试中发现X比Y更快,那么在今天就可能已经过时了。
公平性:
- ReentrantLock可初始化为公平或非公平的锁。
- 大多数情况下非公平锁的性能高于公平锁的性能。
- 基于公平锁,非公平锁及ConcurrentHashMap对HashMap进行吞吐量测试。
- 当持有锁的时间相对较长,或者请求锁的平均时间间隔较长,那么应该使用公平锁。
在Synchronized和ReentrantLock之间作出选择:
- 在一些内置锁无法满足需求的情况下,ReentrantLock可以作为一种高级工具。当需要一些高级功能时才应该使用ReentrantLock,这些功能包括:可定时,可轮询,可中断,公平队列,及非块结构的锁。否则还是应该优先使用synchronized.
读--写锁:
//读写锁允许同时多个线程读, 或最多一个线程写
public interface ReadWriteLock {
Lock readLock();
Lock writeLock();
}
- 读写锁的可选实现:
1. 释放优先。当写入锁释放后,应该优先选择读线程,写线程,还是最先发出请求的线程?
2. 读线程插队。锁由读线程持有,写线程再等待,再来一个读线程,是继续让读线程访问,还是让写线程访问.
3. 重入性。读取锁和写入锁是否可重入?
4. 降级。将写入锁降级为读取锁。
5. 升级。将读取锁升级为写入锁。
- 当锁的持有时间较长并且大部分操作都不会修改被守护的资源时,可用读写锁提高并发性。
/**
* 读写锁来包装Map
*/
public class ReadWriteMap<K, V> {
private final Map<K, V> map;
private final ReadWriteLock lock = new ReentrantReadWriteLock();
private final Lock r = lock.readLock();
private final Lock w = lock.writeLock();
public ReadWriteMap(Map<K, V> map){
this.map = map;
}
// 其他写操作...
public V put(K key, V value){
w.lock(); //请求写锁
try{
return map.put(key, value);
} finally{
w.unlock(); //勿忘
}
}
// 其他读操作...
public V get(K key){
r.lock(); //请求读锁
try{
return map.get(key);
} finally{
r.unlock(); //勿忘
}
}
}
- 对ArrayList使用ReentrantLock和ReadWriteLock测试吞吐量。
不吝指正。