java并发编程(十一)显式锁

Lock与ReentrantLock

Lock接口与内置锁机制不同,Lock提供了一种可轮询,可定时,可中断的锁。且所有的加锁解锁操作都是显式的。

ReentrantLock是Lock接口的默认实现类。Lock接口定义的方法如下所示:

  • void lock() 获取锁。如果锁不可用,出于线程调度目的,将禁用当前线程,并且在获得锁之前,该线程将一直处于休眠状态
  • void lockInterruptibly() 如果当前线程未被中断,则获取锁。这个方法能够在获取锁的同时保持线程对中断请求的响应。
    之前博客说不能响应中断的时候说过,如果一个线程正在等待内置锁,那么这个线程将不可中断,但是当使用lock.lockInterruptibly()方法获取锁时,能够避免这种情况。
  • Condition newCondition() 返回绑定到此 Lock 实例的新 Condition 实例
  • boolean tryLock() 仅在调用时锁为空闲状态才获取该锁。如果锁可用,则获取锁,并立即返回值 true。如果锁不可用,则此方法将立即返回值 false。
    通过这个方法可以实现轮询的方式获取锁,如果线程不能获得这个锁返回false,那么这个线程会释放他所有已经获得的锁,然后重新尝试获取所有的锁,这种方式可以避免死锁
  • boolean tryLock(long time, TimeUnit unit) 如果锁在给定的等待时间内空闲,并且当前线程未被中断,则获取锁。
    通过设置等待时间可以实现定时锁,如果在固定时间内无法获取到锁将会释放全部锁,重新尝试获取锁。
  • void unlock() 释放锁。在等待条件前,锁必须由当前线程保持。调用 Condition.await() 将在等待前以原子方式释放锁,并在等待返回前重新获取锁。

ReadWriteLock与ReentrantReadWriteLock

读写锁:可以被多个读者访问或者被一个写者访问。读写锁提供读写分离功能。
ReentrantReadWriteLock是ReadWriteLock的默认实现类。
它内部有 Lock readLock();和Lock writeLock();两个方法用来创建Lock锁。readLock用于读操作时加锁,writeLock用于写操作加锁。
特性:

  • 读-读不互斥:读读之间不阻塞。
  • 读-写互斥:读阻塞写,写也会阻塞读。
  • 写-写互斥:写写阻塞。

synchronized 和显示锁的对比

相同点: L显示锁能完成synchronized所实现的所有功能;
不同点:

  • 公平性:ReentrantLock和ReentrantReadWriteLock的构造函数中都提供了公平锁和非公平锁两种方式。默认创建非公平锁。
  • 性能:在1.6之前Lock的性能要比synchronized好很多,但是1.6以后二者的性能差距已经不大了,而且随着近几个版本对synchronized的不断优化二者之间的性能已经没有太大差别。除了被频繁读取的数据以外ReadWriteLock的性能都是要略差的。
  • 功能:显示锁除了拥有synchronized的全部功能外还提供了一种可轮询,可定时,可中断的锁。且所有的加锁解锁操作都是显式的。要存在ReadWriteLock可以实现读写锁。

synchronized 和显示锁如何选择
通过对比我们发现尽管synchronized目前的性能已经不差于lock锁了,但是有着很多synchronized无法实现的新功能,是不是意味着我们要抛弃synchronized而全部使用显式锁呢?
答案是否定的,

  • 由于显式锁需要手动的获取和释放锁,这即是优点也是缺点。这种方式更灵活也更危险。
  • synchronied被更多的程序员熟悉,且其代码更加简洁紧凑。
  • 由于synchronied是JVM的内置属性,未来jdk更有可能去提高synchronied的性能,对其做各种优化。

综上所述,我们可以知道如何选择synchronized 和显示锁。
我们应该将显式锁当成加锁的一种高级工具,只有当使用显示锁的一些高级功能时才使用,比如定时锁,轮询锁,可中断锁,读写锁等。如果不需要这些高级功能时我们首要选择还应该是使用内置锁。

发布了56 篇原创文章 · 获赞 4 · 访问量 3万+

猜你喜欢

转载自blog.csdn.net/xs925048899/article/details/104691557