《Java并发编程实战》---性能与可伸缩性---显式锁

Java 5.0之前,在协调线程对共享数据的访问时可以使用的机制只有synchronized和volatile两种,java 5.0增加了一种新的机制:ReentrantLock;但是ReentrantLock并不是代替内置锁,而是作为一种可选择的高级功能;

1:Lock与ReentrantLock

Lock接口定义了一组抽象的加锁操作,与内置加锁机制不同,Lock提供了一种无条件的、可轮询的、定时的以及可中断的锁获取操作,所有加锁和解锁方法都是显示的。

Lock的实现中必须提供与内置加锁相同的内存可见性语义,但是在加锁语义、顺序保证以及性能特性方面有所不同;

Method Summary
 voidlock() 
          Acquires the lock.
 voidlockInterruptibly() 
          Acquires the lock unless the current thread is interrupted.
 ConditionnewCondition() 
          Returns a new Condition instance that is bound to this Lock instance.
 booleantryLock() 
          Acquires the lock only if it is free at the time of invocation.
 booleantryLock(long time, TimeUnit unit) 
          Acquires the lock if it is free within the given waiting time and the current thread has not been interrupted.
 voidunlock() 
          Releases the lock.
 

ReentrantLock实现了Lock接口,并且提供与synchronized相同的互斥性和内存可见性,与synchronized提供了可重入的加锁语义;

ReentrantLock还提供了Lock接口定义的获取锁的模式,与synchronized相比,为处理锁不可用性问题提供了更高的灵活性;

为什么要创建一种与内置锁如此相似的新的加锁机制?在大多数情况下,内置锁都能很好的工作,但是在功能上存在一些局限性,例如,无法中断一个正在等待获取锁的线程、或者无法在请求获取一个锁时无限的等待下去。内置锁必须在获取该锁的代码块中释放,这就简化了编码工作,并且与异常处理操作实现了很好的交互,但却无法实现非阻塞结构的加锁规则;

1.1 轮询锁与定时锁

可定时的与可轮询的锁获取模式是由tryLock方法实现的,与无条件的锁获取方式相比,它具有跟完善的错误恢复机制。在内置锁中,死锁是一个严重的问题,恢复程序的唯一方法是重启程序,防止死锁的唯一方法是在构造程序时避免出现不一致的锁顺序。可定时与可轮询的锁提供了另外一种选择:避免死锁发生;
如果不能获得所需要的锁,那么可以使用可定时的活可轮询的锁获取方式,从而重新获得控制权,它会释放已经获得的锁,然后重试获取所有的锁。

1.2可中断的锁获取操作

内置锁不能响应中断,lockInterruptibly方法能够在获取锁的同时保持对中断的响应;

2:性能因素考虑

当把ReentrantLock加入到java 5.0时,他能比内置锁提供更好的竞争性能。对于同步原语来说,竞争性能是可伸缩性的关键因素:如果越多的资源消耗在锁的管理和调度上,那么应用程序得到的资源就越少。锁的实现方式越好,那么系统调用和上下文切换越少,并且在共享内存上内存同步通信量越少。
java6使用了改进后的算法来管理内置锁,与ReentrantLock中使用算法类似,有效的提高了性能。两者性能差距不是很大。

3:公平性

在ReentrantLock的构造函数中提供了两种公平性选择:创建一个非公平的锁(默认)或者一个公平的锁。公平锁,线程将按照请求顺序获得锁;非公平锁允许插队:当一个线程请求非公平锁时,如果发出请求的同时该锁的状态变为可用,那么这个线程将跳过队列中所有等待线程获得这个锁;
为什么不希望所有的锁都是公平的?当执行加锁操作时,公平性将由于挂起线程和恢复线程时存在的开销而极大的降低性能。
内置锁与ReentrantLock一样,默认不提供确定的公平性保证;

4:在synchronized和ReentrantLock之间进行选择

ReentrantLock与synchronized在加锁和内存上提供的语义与内置锁相同,此外还提供了一些其他功能,包括定时的锁等待、可中断的所等待、公平性。
ReentrantLock的性能在Java6.0中略有胜出,Java5.0中远远胜出;

内置锁其实也有优势:
现有许多程序都使用了内置锁;不需要显示的释放锁降低风险;此外,在线程转储中能给出在哪些调用帧中获得了哪些锁,并且能够检测和识别发生死锁的线程.
JVM并不知道哪些线程持有ReentrantLock,因此在调试使用ReentrantLock的线程问题时,将起不到帮助作用;
未来可能提升的时内置锁的性能,而不是ReentrantLock。synchronzed是JVM的内置属性,他能执行一些优化,例如对线程封闭的锁对象的锁消除优化,通过增加锁的粒度来消除内置锁的同步。

5:读-写锁

ReentrantLock实现了一种标准的互斥锁:每次最多一个线程持有ReentrantLock。但是对于维护数据的完整性来说,互斥通常是一种过于强硬的加锁规则,也会限制了并发性。
在许多情况下,数据结构上的操作大多都是“读操作”,如果能运行多个读操作的线程能同时访问数据结构,那么将大大提升程序性能;此时,多个读操作可以同时进行,或者只有一个写操作进行,但是读写操作不能够同时进行;
public interface ReadWriteLock{
<span style="white-space:pre">	</span>Lock readLock();
<span style="white-space:pre">	</span>Lock writeLock();
}


例子:
public class ReadWriteMap<K,V>{
<span style="white-space:pre">	</span>private final Map<K,V> map;
<span style="white-space:pre">	</span>private finalReentrantReadWriteLock lock=new ReentrantReadWriteLock();
<span style="white-space:pre">	</span>private final Lock r=lock.readLock();
<span style="white-space:pre">	</span>private final Lock w=lock.writeLock();
<span style="white-space:pre">	</span>public V put(K key,V value){
<span style="white-space:pre">		</span>w.lock();
<span style="white-space:pre">		</span>try{
<span style="white-space:pre">			</span>return map.put(key,value);
<span style="white-space:pre">		</span>}finally{
<span style="white-space:pre">			</span>w.unlock();
<span style="white-space:pre">		</span>}
<span style="white-space:pre">	</span>}
<span style="white-space:pre">	</span>public V get(K key){
<span style="white-space:pre">		</span>r.lock();
<span style="white-space:pre">		</span>try{
<span style="white-space:pre">			</span>return map.get(key);
<span style="white-space:pre">		</span>}finally{
<span style="white-space:pre">			</span>r.unlock();
<span style="white-space:pre">		</span>}<span style="white-space:pre">	</span>
<span style="white-space:pre">	</span>}
}

6:总结

与内置锁相比,显式的Lock提供了一些扩展功能,在处理锁的不可用性方面有着更高的灵活性,并且对队列行有着更好的控制(意思应该是请求线程队列)。但是ReentantLock不能完全代替synchronized,只有在无法满足需求时才应该使用它;
读-写锁允许多个线程并发的访问被保护的对象,当访问以读取操作为主的数据结构时,他能提供程序的可伸缩性
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值