1.基本概念

1.1.什么事可重入锁?

ReentrantLock是Java.util.concurrent.locks包下的一个类,它实现Lock 接口,提供比 synchronized 更灵活、强大的锁机制。"可重入"意味着同一个线程可以多次获取同一把锁,而不会被阻塞。

1.2.为什么需要ReentranLock?

相比于synchronized而言,ReentranLock提供了以下优势:

(1)同时支持公平锁和非公平锁;

(2)获取锁的过程,可以中断;

(3)获取锁的过程中,可以设置超时;

(4)支持多个条件变量(Condition);

(5)支持非阻塞地方尝试获取锁;

2.核心方法

2.1.锁的操作方法

2.1.1构造方法

// 创建一个非公平锁
ReentrantLock rtl= new ReentrantLock() 

// 根据公平策略创建锁
ReentrantLock rtl= new ReentrantLock(boolean fair) 

2.1.2.其他方法

方法描述是否可中断是否阻塞是否带超时
void lock()获取锁,若锁被占用则阻塞等待❌ 不可中断✅ 阻塞❌ 无超时
void lockInterruptibly()获取锁,阻塞时可响应中断✅ 可中断✅ 阻塞❌ 无超时
boolean tryLock()尝试获取锁,立即返回成功或失败❌ 不可中断❌ 非阻塞❌ 无超时
boolean tryLock(long timeout, TimeUnit unit)尝试获取锁,带超时等待✅ 可中断✅ 超时前阻塞✅ 可设超时
void unlock()释放锁---
int getHoldCount()返回当前线程重入锁的次数---
boolean isHeldByCurrentThread()检查当前线程是否持有锁---
boolean isLocked()检查锁是否被任意线程持有---
boolean isFair()判断是否为公平锁---
getQueuedThreads()返回等待获取锁的线程集合---
int getQueueLength()返回等待获取锁的线程数---
boolean hasWaiters(Condition condition)检查是否有线程在等待指定条件---

2.2条件变量方法

根据创建的ReentrantLock对象,创建条件变量

ReentrantLock lock=new ReentrantLock();
Condition condition = lock.newCondition();

补充:Condition的主要用法

 主要方法

方法作用对应Object方法
await()释放锁并等待wait()
await(long time, TimeUnit unit)限时等待wait(long timeout)
awaitNanos(long nanosTimeout)纳秒级限时等待-
awaitUninterruptibly()不可中断的等待-
signal()唤醒一个等待线程notify()
signalAll()唤醒所有等待线程notifyAll()

3.使用案例

3.1锁的基础使用

package Lock;

import java.util.concurrent.locks.ReentrantLock;

//ReentrantLock
public class Demo01 {
    public static void main(String[] args) {
        Counter counter = new Counter();
        Counter counter2 = new Counter();

        Thread t1=new Thread(new Runnable() {
            @Override
            public void run() {
              counter.add();
            }
        });

        Thread t2=new Thread(new Runnable() {
            @Override
            public void run() {
              counter2.sub();
            }
        });

        t1.start();
        t2.start();
        try {
            t1.join();
            t2.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("main=>"+counter.countValue);

    }
}
class Counter {
    public static int countValue = 0;

    //锁对象
    public static final ReentrantLock lock = new ReentrantLock();

    public void add() {
        lock.lock();//加锁
        try {
            for (int i = 0; i < 10000; i++) {
                countValue++;
            }
        } finally {
            lock.unlock();//释放锁
        }
    }

    public void sub() {
        lock.lock();
        try {
            for (int i = 0; i < 10000; i++) {
                countValue--;
            }
        } finally {
            lock.unlock();
        }
    }
}

运行结果:

要使用ReentrantLock就要创建锁对象,此段代码两个线程共用了一个锁对象

package Lock;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;

public class Demo02 {
    public static void main(String[] args) {
        TicketPool ticketPool=new TicketPool(20);

        Thread t1=new Thread(ticketPool,"窗口1");
        Thread t2=new Thread(ticketPool,"窗口2");
        Thread t3=new Thread(ticketPool,"窗口3");

        t1.start();
        t2.start();
        t3.start();
    }
}

class TicketPool implements Runnable{
    //当前剩余门票数量
    private int ticketNumber ;

    //锁对象
    private final ReentrantLock lock = new ReentrantLock();

   // 条件对象
    private final Condition condition = lock.newCondition();

    public TicketPool(int ticketNumber) {
        this.ticketNumber = ticketNumber;
    }

    @Override
    public void run() {
        //售票逻辑
        System.out.println(Thread.currentThread().getName()+"准备开始售票");

        lock.lock();
        try {
            while(true){
                if (ticketNumber <=0){
                    System.out.println(Thread.currentThread().getName()+"票已售完");
                    return;
                }else {
                    System.out.println(Thread.currentThread().getName()+"正在出售第"+ticketNumber+"张票,还剩下"+--ticketNumber+"张票");
                    try {
                        condition.await(1, TimeUnit.SECONDS);
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                }
            }
        }finally {
            lock.unlock();
        }
    }
}

4.ReentrantLock内部结构

ReentrantLock实现了 Lock接,Lock接中定义了lock()、unlock() ,tryLock()等相关操作。

ReentrantLock总共有三个内部类:Sync,NofairSync,FairSync

Nonfairsync 类继承了 sync类,表示采用非公平策略获取锁:每一次都尝试获取锁,不会按照公平等待的原则进行等待,不会让等待时间最久的线程获得锁。

Fairsync 类也继承了 sync 类,表示采用公平策略获取锁:当资源空闲时,它总是会先判断同步队列中,是否有等待时间更长的线程,如果存在,则将当前线程加入到等待队列的尾部,实现了公平获取原则。

ReentrantLock 构造函数:默认是采用的非公平策略获取锁。

5.Synchronized和ReentrantLock的区别

特性synchronized (内置锁)ReentrantLock (显式锁)
实现方式JVM 关键字,自动管理JDK 类,需要手动编码实现
锁获取隐式获取和释放必须显式调用 lock() 和 unlock()
可中断性不可中断支持 lockInterruptibly() 可中断等待
公平性非公平锁可选择公平/非公平(构造方法参数)
条件队列单一等待队列可创建多个 Condition 实现精细控制
性能JDK6 后优化,性能相当高竞争时表现更好
锁绑定条件只能绑定一个监视器可绑定多个 Condition
锁状态查询无法查询提供 tryLock()isLocked() 等方法
超时机制不支持支持 tryLock(timeout) 超时获取
代码灵活性语法简单,但控制粒度粗控制精细,但需要手动释放
异常处理自动释放锁必须在 finally 中释放锁
适用场景简单同步场景复杂同步需求(如公平性、可中断、多条件等)
Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐