简述

ReentrantLock 是一个可重入的互斥(/独占)锁,又称为“独占锁”。

ReentrantLock通过自定义队列同步器(AQS-AbstractQueuedSychronized,是实现锁的关键)来实现锁的获取与释放。

其可以完全替代 synchronized 关键字。JDK 5.0 早期版本,其性能远好于 synchronized,但 JDK 6.0 开始,JDK 对 synchronized 做了大量的优化,使得两者差距并不大。

“独占”,就是在同一时刻只能有一个线程获取到锁,而其它获取锁的线程只能处于同步队列中等待,只有获取锁的线程释放了锁,后继的线程才能够获取锁。

“可重入”,就是支持重进入的锁,它表示该锁能够支持一个线程对资源的重复加锁。

该锁还支持获取锁时的公平和非公平性选择。“公平”是指“不同的线程获取锁的机制是公平的”,而“不公平”是指“不同的线程获取锁的机制是非公平的”。

简单实例

import java.util.concurrent.locks.ReentrantLock;
/**
 * Created by zhengbinMac on 2017/3/2.
 */
public class ReenterLock implements Runnable{
    public static ReentrantLock lock = new ReentrantLock();
    public static int i = 0;
    public void run() {
        for (int j = 0;j<100000;j++) {
            lock.lock();
//            lock.lock();
            try {
                i++;
            }finally {
                lock.unlock();
//                lock.unlock();
            }
        }
    }
    public static void main(String[] args) throws InterruptedException {
        ReenterLock reenterLock = new ReenterLock();
        Thread t1 = new Thread(reenterLock);
        Thread t2 = new Thread(reenterLock);
        t1.start();t2.start();
        t1.join();t2.join();
        System.out.println(i);
    }
}

与 synchronized 相比,重入锁有着显示的操作过程,何时加锁,何时释放,都在程序员的控制中。

为什么称作是“重入”?这是因为这种锁是可以反复进入的。将上面代码中注释部分去除注释,也就是连续两次获得同一把锁,两次释放同一把锁,这是允许的。

注意,获得锁次数与释放锁次数要相同,如果释放锁次数多了,会抛出 java.lang.IllegalMonitorStateException 异常;如果释放次数少了,相当于线程还持有这个锁,其他线程就无法进入临界区。

引出第一个问题:为什么 ReentrantLock 锁能够支持一个线程对资源的重复加锁?

除了简单的加锁、解锁操作,重入锁还提供了一些更高级的功能,下面结合实例进行简单介绍:

中断响应(lockInterruptibly)

对于 synchronized 来说,如果一个线程在等待锁,那么结果只有两种情况,获得这把锁继续执行,或者线程就保持等待。

而使用重入锁,提供了另一种可能,这就是线程可以被中断。也就是在等待锁的过程中,程序可以根据需要取消对锁的需求。

下面的例子中,产生了死锁,但得益于锁中断,最终解决了这个死锁:

 1 import java.util.concurrent.locks.ReentrantLock;
 2 /**
 3  * Created by zhengbinMac on 2017/3/2.
 4  */
 5 public class IntLock implements Runnable{
 6     public static ReentrantLock lock1 = new ReentrantLock();
 7     public static ReentrantLock lock2 = new ReentrantLock();
 8     int lock;
 9     /**
10      * 控制加锁顺序,产生死锁
11      */
12     public IntLock(int lock) {
13         this.lock = lock;
14     }
15     public void run() {
16         try {
17             if (lock == 1) {
18                 lock1.lockInterruptibly(); // 如果当前线程未被 中断,则获取锁。
19                 try {
20                     Thread.sleep(500);
21                 } catch (InterruptedException e) {
22                     e.printStackTrace();
23                 }
24                 lock2.lockInterruptibly();
25                 System.out.println(Thread.currentThread().getName()+",执行完毕!");
26             } else {
27                 lock2.lockInterruptibly();
28                 try {
29                     Thread.sleep(500);
30                 } catch (InterruptedException e) {
31                     e.printStackTrace();
32                 }
33                 lock1.lockInterruptibly();
34                 System.out.println(Thread.currentThread().getName()+",执行完毕!");
35             }
36         } catch (InterruptedException e) {
37             e.printStackTrace();
38         } finally {
39             // 查询当前线程是否保持此锁。
40             if (lock1.isHeldByCurrentThread()) {
41                 lock1.unlock();
42             }
43             if (lock2.isHeldByCurrentThread()) {
44                 lock2.unlock();
45             }
46             System.out.println(Thread.currentThread().getName() + ",退出。");
47         }
48     }
49     public static void main(String[] args) throws InterruptedException {
50         IntLock intLock1 = new IntLock(1);
51         IntLock intLock2 = new IntLock(2);
52         Thread thread1 = new Thread(intLock1, "线程1");
53         Thread thread2 = new Thread(intLock2, "线程2");
54         thread1.start();
55         thread2.start();
56         Thread.sleep(1000);
57         thread2.interrupt(); // 中断线程2
58     }
59 }
View Code

相关文章: