ReentrantLock
相对于synchronized,它具备的特点是:
- 可中断
- 可以设置超时时间
- 可以设置为公平锁
- 支持多个条件变量
与synchronized一样,都支持可重入。
基本语法:
| 12
 3
 4
 5
 6
 7
 8
 
 | reentrantLock.lock();
 try{
 
 }finally{
 
 reentrantLock.unlock();
 }
 
 | 
1、ReentrantLock的特点
1、可重入
可重入是指:同一个线程如果首次获得了这把锁,那么因为它是这把锁的拥有者,因此由权力再次获取这把锁。
下面这个例子:主线程调用方法method1(),在method1中获取lock锁。
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 
 | public static ReentrantLock lock = new ReentrantLock();
 public static void method1(){
 try{
 lock.lock();
 System.out.println("方法1 执行");
 method2();
 }finally {
 lock.unlock();
 }
 }
 public static void method2(){
 try{
 lock.lock();
 System.out.println("方法2 执行");
 method3();
 }finally {
 lock.unlock();
 }
 }
 public static void method3(){
 try {
 lock.lock();
 System.out.println("方法3 执行");
 }finally {
 lock.unlock();
 }
 }
 
 public static void main(String[] args) {
 method1();
 }
 
 | 

2、可打断
lock.lockInterruptibly()方法。
如果没有竞争那么此方法就会获取lock对象锁。
如果有竞争就进入阻塞队列,可以被其他线程用 interruput 方法打断。
下面这个例子:主线程先获取锁,t1线程进入阻塞队列。主线程执行中断。
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 
 | public static void main(String[] args) throws InterruptedException {ReentrantLock lock = new ReentrantLock();
 
 Thread t1 = new Thread(() -> {
 System.out.println("t1线程启动");
 
 try{
 lock.lockInterruptibly();
 } catch (InterruptedException e) {
 e.printStackTrace();
 System.out.println("等待的过程中被打断");
 return;
 }
 
 try {
 System.out.println("获得了锁");
 }finally {
 lock.unlock();
 }
 
 
 },"t1");
 
 lock.lock();
 System.out.println("主线程获得了锁");
 t1.start();
 
 try {
 sleep(10);
 t1.interrupt();
 System.out.println("执行中断");
 }finally {
 lock.unlock();
 }
 }
 
 | 

3、 锁超时
当线程在尝试获取锁时,可能锁已经被其他线程占了。
这是我们可以设置一段时间,在这段时间内,线程可以获得锁。
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 
 | public static void main(String[] args) throws InterruptedException {ReentrantLock lock = new ReentrantLock();
 
 Thread t1 = new Thread(() -> {
 System.out.println("t1线程启动");
 
 try {
 if(!lock.tryLock(1, TimeUnit.SECONDS)){
 System.out.println("获取锁等待1s后失败,返回");
 return;
 }
 } catch (InterruptedException e) {
 e.printStackTrace();
 }
 
 try{
 System.out.println("t1线程获取锁");
 }finally {
 lock.unlock();
 }
 },"t1");
 
 lock.lock();
 System.out.println("主线程获取了锁");
 t1.start();
 
 try{
 sleep(3000);
 }finally {
 System.out.println("主线程释放锁");
 lock.unlock();
 }
 }
 
 | 

4、公平锁
ReentrantLock 默认是不公平的
| 12
 3
 4
 5
 6
 7
 
 | public ReentrantLock() {sync = new NonfairSync();
 }
 
 public ReentrantLock(boolean fair) {
 sync = fair ? new FairSync() : new NonfairSync();
 }
 
 | 
不公平锁
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 
 | public static void main(String[] args) throws InterruptedException {
 ReentrantLock lock = new ReentrantLock(false);
 
 lock.lock();
 
 
 for (int i = 0; i < 500; i++) {
 new Thread(() -> {
 lock.lock();
 try{
 System.out.println(Thread.currentThread().getName() + "Running");
 }finally {
 lock.unlock();
 }
 },"t" + i).start();
 }
 
 
 Thread.sleep(1000);
 new Thread(() -> {
 System.out.println(Thread.currentThread().getName() + " start");
 lock.lock();
 try{
 System.out.println(Thread.currentThread().getName() + " Running");
 }finally {
 lock.unlock();
 }
 },"强行插入").start();
 
 lock.unlock();
 }
 
 | 

公平锁
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 
 | public static void main(String[] args) throws InterruptedException {
 
 ReentrantLock lock = new ReentrantLock(true);
 
 lock.lock();
 
 
 for (int i = 0; i < 500; i++) {
 new Thread(() -> {
 lock.lock();
 try{
 System.out.println(Thread.currentThread().getName() + "Running");
 }finally {
 lock.unlock();
 }
 },"t" + i).start();
 }
 
 
 Thread.sleep(1000);
 new Thread(() -> {
 System.out.println(Thread.currentThread().getName() + " start");
 lock.lock();
 try{
 System.out.println(Thread.currentThread().getName() + " Running");
 }finally {
 lock.unlock();
 }
 },"强行插入").start();
 
 lock.unlock();
 
 }
 
 | 

条件变量
在synchronized中也有条件变量。
某一个线程获取锁之后,调用wait()方法。就会进入waitiing队列中。(这就是为什么调用wait()方法必须先所得锁)
但是synchronized中只有一个wait队列。而ReentrantLock支持多个wait队列。
如何使用:
- await前需要获得锁
- await执行后,会释放锁,进入conditionObject等待
- await的线程被唤醒(或打断、或超时)去重新竞争lock锁
- 竞争lock锁成功后,从await后继续执行
例子:有两个员工需要等到烟和早餐才能工作。
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 
 | public class test1 {
 static ReentrantLock lock = new ReentrantLock();
 
 static Condition waitCigaretteQueue = lock.newCondition();
 static Condition waitbreakfastQueue = lock.newCondition();
 
 static volatile boolean hasCigrette = false;
 static volatile boolean hasBreakfast = false;
 
 
 public static void main(String[] args) throws InterruptedException {
 new Thread(() -> {
 try {
 lock.lock();
 
 while(!hasCigrette){
 try{
 waitCigaretteQueue.await();
 System.out.println("t1线程正在等烟");
 } catch (InterruptedException e) {
 e.printStackTrace();
 }
 }
 
 System.out.println("t1线程等到了他的烟");
 }finally {
 lock.unlock();
 }
 },"t1").start();
 
 new Thread(() -> {
 try {
 lock.lock();
 
 while (!hasBreakfast) {
 try {
 System.out.println("t2线程正在等早餐");
 waitbreakfastQueue.await();
 
 } catch (InterruptedException e) {
 e.printStackTrace();
 }
 }
 
 System.out.println("t2等到了他的早餐");
 } finally {
 lock.unlock();
 }
 },"t2").start();
 
 sleep(1);
 sendBreakfast();
 sleep(1);
 sendCigarette();
 
 }
 
 
 private static void sendCigarette() {
 lock.lock();
 try {
 System.out.println("t1线程烟到了");
 hasCigrette = true;
 waitCigaretteQueue.signal();
 } finally {
 lock.unlock();
 }
 }
 private static void sendBreakfast() {
 lock.lock();
 try {
 System.out.println("t2线程早餐到了");
 hasBreakfast = true;
 waitbreakfastQueue.signal();
 } finally {
 lock.unlock();
 }
 }
 
 }
 
 |