欢迎您访问 最编程 本站为您分享编程语言代码,编程技术文章!
您现在的位置是: 首页

Java多线程实战(十二): 明白的锁机制详解

最编程 2024-07-28 19:41:51
...

显示锁:

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测试吞吐量。

不吝指正。

推荐阅读