Java并发读书笔记:Lock与ReentrantLock

Lock位于java.util.concurrent.locks包下,是一种线程同步机制,就像synchronized块同样。可是,Locksynchronized块更灵活、更复杂。java

话很少说,咱们直接来看官方文档对Lock接口相关概念及功能的描述,今天又是看英文文档,翻译理解的一天。编程

1、Lock继承关系

2、官方文档解读


3、Lock接口方法解读

void lock()api

获取锁。若是锁不可用,则当前线程将出于线程调度目的而禁用,并处于休眠状态,直到得到锁为止。安全

void lockInterruptibly() throws InterruptedException;并发

若是当前线程未被中断,则获取锁。若是锁可用,则获取锁并当即返回。函数

若是锁不可用,出于线程调度目的,将禁用当前线程,该线程将一直处于休眠状态。性能

下面两种情形会让当前线程中止休眠状态:优化

  • 锁由当前线程获取。.net

  • 其余一些线程中断当前线程,而且支持对锁获取的中断。线程

当前线程出现下面两种状况时,将抛出InterruptedException,并清除当前线程的中断状态。

  • 当前线程在进入此方法时,已经设置为中断状态。

  • 当前线程在获取锁时被中断,而且支持对锁获取中断。

boolean tryLock();

尝试获取锁,若是锁处于空闲状态,则获取锁,并当即返回true。若是锁不可用,则当即返回false。

该方法的典型使用:

Lock lock = ...;
    //确保锁在被获取时被解锁
    if (lock.tryLock()) {
        try {
            // manipulate protected state
        } finally {
            lock.unlock();
        }
    } else {
        // perform alternative actions
    }

boolean tryLock(long time, TimeUnit unit) throws
InterruptedException;

该方法为tryLock()的重载方法,两个参数分别表示为:

  • time:等待锁的最长时间
  • unit:时间单位

若是在给定的等待时间内是空闲的而且当前线程没有被中断,则获取锁。若是锁可用,则此方法当即获取锁并返回true,若是锁不可用,出于线程调度目的,将禁用当前线程,该线程将一直处于休眠状态。

下面三种情形会让当前线程中止休眠状态:

  • 锁由当前线程获取。

  • 其余一些线程中断当前线程,而且支持对锁获取的中断。
  • 到了指定的等待时间。

当前线程出现下面两种状况时,将抛出InterruptedException,并清除当前线程的中断状态。

  • 当前线程在进入此方法时,已经设置为中断状态。

  • 当前线程在获取锁时被中断,而且支持对锁获取中断。

若是指定的等待时间超时,则返回false值。若是时间小于或等于0,则该方法永远不会等待。

void unlock()

释放锁,与lock()、tryLock()、tryLock(long , TimeUnit)、lockInterruptibly()相对应。

Condition newCondition()

返回绑定到此锁实例的Condition实例。当前线程只有得到了锁,才能调用Condition实例的await()方法,并释放锁。

4、重要实现类ReentrantLock

顾名思义,ReentrantLock是重入锁,关于这个重入锁,以前涉及过一些知识,在这里作整合,并稍微地补充一下。

ReentrantLock位于java.util.concurrent(J.U.C)包下,是Lock接口的实现类。基本用法与synchronized类似,都具有可重入互斥的特性,但拥有扩展的功能。

RenntrantLock推荐的基本写法:

class X {
    //定义锁对象
    private final ReentrantLock lock = new ReentrantLock();
    // ...
    //定义须要保证线程安全的方法
    public void m() {
        //加锁
        lock.lock();  
        try{
        // 保证线程安全的代码
        }
        // 使用finally块保证释放锁
        finally {
            lock.unlock()
        }
    }
}

一、API层面的锁

ReentrantLock表现为API层面的互斥锁,经过lock()unlock()方法完成,是显式的,而synchronized表现为原生语法层面的互斥锁,是隐式的。

二、可重入的

重进入意味着:任意线程在获取到锁以后可以再次获取该锁而不会被锁阻塞synchronized和Reentrant都是可重入的,隐式显式之分。

实现可重入须要解决的两个关键部分:

  1. 锁须要去识别获取锁的线程是不是当前占据锁的线程,若是是的话,就成功获取。
  2. 锁获取一次,内部锁计数器须要加一,释放一次减一,计数为零表示为成功释放锁。

三、可公平的

关于锁公平的部分,官方文档是这样描述的(英文我就不贴了),词汇较简单,我试着翻译一下:

Reentrant类的构造函数接受一个可选的公平性参数fair。这时候就出现两种选择:

  • 公平的(fair == true):保证等待时间最长的线程优先获取锁,即FIFO。
  • 非公平的(fair == false):此锁不保证任何特定的访问顺序。

公平锁每每体现处的整体吞吐量比非公平锁要低,也就是更慢。

锁的公平性并不保证线程调度的公平性,但公平锁可以减小"饥饿"发生的几率。

须要注意的是:不定时的tryLock()方法不支持公平性设置。若是锁可用,即便其余线程等待时间比它长,它也会成功得到锁。

四、等待可中断

当持有线程长期不释放锁的时候,正在等待的线程可以选择放弃等待处理其余事情

五、锁绑定

一个ReentrantLock对象能够经过newCondition()同时绑定多个Condition对象


JDK1.6以前,ReentrantLock在性能方面是要领先于synchronized锁的,可是JDK1.6及以后版本实现了各类锁优化技术,可参考:
聊聊并发Java SE1.6中的Synchronized,后续性能改进会更加偏向于原生的synchronized。


参考资料: 《深刻理解Java虚拟机》周志明 《Java并发编程的艺术》方腾飞

相关文章
相关标签/搜索