1.简述

ReentrantLock 是一个可重入的互斥(/独占)锁,又称为“独占锁”。
其可以完全替代 synchronized 关键字。JDK 1.5.0 引入的,其性能远好于 synchronized,但 JDK 1.6.0 开始,JDK 对 synchronized 做了大量的优化,使得两者差距并不大。但其提供了超出synchonized的其他高级功能(例如,中断锁等候、条件变量等),并且使用ReentrantLock比synchronized能获得更好的可伸缩性。也更灵活。

2.特性

  • “独占”,指在同一时刻只能有一个线程获取到锁,而其它获取锁的线程只能处于同步队列中等待,只有获取锁的线程释放了锁,后续的线程才能够获取锁。
  • “可重入”,就是支持重进入的锁,它表示该锁能够支持一个线程对资源的重复加锁。
  • “选择性”,支持获取锁时的公平和非公平性选择。“公平” 指不同的线程获取锁的机制是公平的”,而“不公平” 指不同的线程获取锁的机制是非公平的”。

3.ReentrantLock常用方法

3.1 方法介绍

序号

方法名

返回值

含义

1

lock()

void

当前线程-加锁

2

unlock()

void

当前线程-解锁

3

tryLock()

Boolean

尝试获得锁,仅在调用时锁未被线程占用,获得锁

4

tryLock(long timeout, TimeUnit unit)

Boolean

限时的锁等待,一定时间内若扔未获得则返回false

5

isLocked

Boolean

是否锁定

6

isFair()

Boolean

是否公平

7

lockInterruptibly()

Void

如果当前线程未被中断,获取锁

8

isHeldByCurrentThread()

Boolean

当前线程是否保持锁锁定,线程的执行lock方法的前后分别是false和true

9

hasQueuedThreads()

Boolean

是否有线程等待此锁

10

getHoldCount()

int

当前线程保持此锁的次数,即执行此线程执行lock方法的次数

11

getQueueLength()

int

返回正等待获取此锁的线程估计数,例如:启动10个线程,1个线程获得锁,此时返回的是9

12

getWaitQueueLength(Condition condition)

i

返回等待与此锁相关的给定条件的线程估计数。比如10个线程,用同一个condition对象,并且此时这10个线程都执行了condition对象的await方法,那么此时执行此方法返回10

13

hasWaiters(Condition condition)

Boolean

查询是否有线程等待与此锁有关的给定条件(condition),对于指定contidion对象,有多少线程执行了condition.await方法

3.2 tryLock和lock和lockInterruptibly的区别

  • tryLock 能获得锁就返回true,不能就立即返回false
  • tryLock(long timeout,TimeUnit unit),可以增加时间限制,如果超过该时间段还没获得锁,返回false
  • lock 能获得锁就返回true,不能的话一直等待获得锁
  • lock和lockInterruptibly,如果两个线程分别执行这两个方法,但此时中断这两个线程,前者不会抛出异常,而后者会抛出异常

4.使用实例

注:在实例中使用了int i ++操作作为延时代码,i++是非线程安全的,
详细了解可参考

4.1 lock与unlock

标准使用格式示例

//创建锁
private static ReentrantLock syncLock = new ReentrantLock();

public void test() {
       try {
                syncLock.lock();
                 // todo 
            } finally {
                //解锁,勿忘,防止死锁
                syncLock.unlock();
            }
    }

测试代码实例

public static void main(String[] args) throws Exception {
        //创建线程池
        ScheduledExecutorService scheduler = new ScheduledThreadPoolExecutor(20);
        for (int i = 0; i < 10; i++) {
            scheduler.execute(() -> {
                long start = System.currentTimeMillis();
                //尝试加锁
                if (syncLock.tryLock()) {
                    try {
                        long end = System.currentTimeMillis();
                        System.out.println("线程:" + Thread.currentThread().getName() + "获取锁成功了!" + "等待毫秒数:" + (end - start));
                        // 模拟业务代码 耗时
                        Thread.sleep(100);
                    } catch (Exception e) {
                        e.printStackTrace();
                    } finally {
                        //解锁
                        syncLock.unlock();
                    }
                } else {
                    long end = System.currentTimeMillis();
                    System.out.println("线程:" + Thread.currentThread().getName() + "获取锁失败了!" + "等待毫秒数:" + (end - start));
                }
            });
        }
        scheduler.shutdown();
        //保证前面的线程都执行完
        while (Thread.activeCount() > 2) {
            Thread.yield();
        }

    }

测试结果

java的reentrantlock和lock有什么区别 java中reentrantlock_java锁

4.1 trylock

测试实例1
注:trylock 获取不到锁立马返回

package com.example.reent.lock;

import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.locks.ReentrantLock;
/**
 * @author 码农猿
 */
public class ReentrantLockTest2 {
    private static ReentrantLock syncLock = new ReentrantLock();

    public static void main(String[] args) throws Exception {
        //创建线程池
        ScheduledExecutorService scheduler = new ScheduledThreadPoolExecutor(20);
        for (int i = 0; i < 10; i++) {
            scheduler.execute(() -> {
                //加锁
                if (syncLock.tryLock()) {
                    try {

                        for (int j = 0; j < 10; j++) {
                            System.out.println("线程:" + Thread.currentThread().getName() + "获取锁成功了!");
                        }
                        // 模拟业务代码 耗时
                        Thread.sleep(100);
                    } catch (Exception e) {
                        e.printStackTrace();
                    } finally {
                        //解锁
                        syncLock.unlock();
                    }
                } else {
                    System.out.println("线程:" + Thread.currentThread().getName() + "获取锁失败了!");

                }
            });
        }
        scheduler.shutdown();
        //保证前面的线程都执行完
        while (Thread.activeCount() > 2) {
            Thread.yield();
        }  
    }
}

测试结果

java的reentrantlock和lock有什么区别 java中reentrantlock_java锁_02

测试实例12
注:trylock(long timeout, TimeUnit unit) 设置获取锁超时时间

public static void main(String[] args) throws Exception {
        //创建线程池
        ScheduledExecutorService scheduler = new ScheduledThreadPoolExecutor(20);
        for (int i = 0; i < 10; i++) {
            scheduler.execute(() -> {
                long start = System.currentTimeMillis();
                try {
                    //等待 205毫秒尝试加锁
                    if (syncLock.tryLock(205, TimeUnit.MILLISECONDS)) {
                        try {
                            long end = System.currentTimeMillis();
                            System.out.println("线程:" + Thread.currentThread().getName() + "获取锁成功了!" + "等待毫秒数:" + (end - start));
                            // 模拟业务代码 耗时
                            Thread.sleep(100);
                        } catch (Exception e) {
                            e.printStackTrace();
                        } finally {
                            //解锁
                            syncLock.unlock();
                        }
                    } else {
                        long end = System.currentTimeMillis();
                        System.out.println("线程:" + Thread.currentThread().getName() + "获取锁失败了!" + "等待毫秒数:" + (end - start));
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
            });
        }
        scheduler.shutdown();
        //保证前面的线程都执行完
        while (Thread.activeCount() > 2) {
            Thread.yield();
        }
    }

结果图示

java的reentrantlock和lock有什么区别 java中reentrantlock_加锁_03

总结

优点:
1.加锁和释放锁独立,可以分开来控制。
2.tryLock的方法可以尝试加锁,不会像sychnorized一致阻塞。
3.实现了公平锁和非公平锁。sychnorized只能是非公平锁。

缺点:
1.增加了代码的复杂度。
2.相比sychnorized的自动加锁和释放锁,Lock需要手动,容易忘记,从而出现重大的隐患。