java并发学习23:ReentrantLock介绍使用

本文深入探讨了ReentrantLock锁的特点及使用方式,包括其相对于synchronized的优势如可中断、超时支持、公平锁选项以及对多个条件变量的支持,并通过实例说明了如何实现可重入、打断、超时等功能。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1、与synchronized相比
  • 可中断
  • 可以设置超时时间
  • 可以设置为公平锁
  • 支持多个条件变量

与synchronized一样,都支持可重入

基本语法:

// 获取锁
reentrantLock.lock();
try {
    // 临界区
} finally {
    // 释放锁
    reentrantLock.unlock();
}
2、可重入

可重入是指同一个线程如果首次获得了这把锁,那么因为它是这把锁的拥有者,因此有权利再次获取这把锁

如果是不可重入锁,那么第二次获得锁时,自己也会被锁挡住

static ReentrantLock lock = new ReentrantLock();
public static void main(String[] args) {
    method1();
}
public static void method1() {
    lock.lock();
    try {
        log.debug("execute method1");
        method2();  
    } finally {
        lock.unlock();   
    }
}
public static void method2() {
    lock.lock();
    try {
        log.debug("execute method2");
        method3();    
    } finally {
        lock.unlock(); 
    }
}
public static void method3() {
    lock.lock();
    try {
        log.debug("execute method3"); 
    } finally {
        lock.unlock(); 
    }
}
3、可打断

lock.lockInterruptibly();//获取锁,同时可以被其他线程打断

static ReentrantLock lock = new ReentrantLock();
public static void main(String[] args) {
    new Thread(() -> {
       method1();  
    },"t1")
}
public static void method1() {
    //如果没有竞争那么方法就会获取lock对象锁
    //如果没有竞争就进入阻塞队列,可以被其他线程用interrupt 方法打断
    lock.lockInterruptibly();
    try {
        log.debug("execute method1");
        method2();  
    } finally {
        lock.unlock();   
    }
}
4、锁超时
4.1 立刻失败
ReentrantLock lock = new ReentrantLock();
Thread t1 = new Thread(() -> {
    log.debug("启动...");
    if (!lock.tryLock()) {
        log.debug("获取立刻失败,返回");
        return;  
    }
    try {
        log.debug("获得了锁"); 
    } finally {
        lock.unlock();
    }
}, "t1");
lock.lock();
log.debug("获得了锁")
4.2 超时失败
ReentrantLock lock = new ReentrantLock();
Thread t1 = new Thread(() -> {
    log.debug("启动...");
    try {
        if (!lock.tryLock(1, TimeUnit.SECONDS)) {
            log.debug("获取等待 1s 后失败,返回");
            return;       
        }   
    } catch (InterruptedExceptione) {
        e.printStackTrace(); 
    }
    try {
        log.debug("获得了锁");   
    } finally {
        lock.unlock();
    }
}, "t1");
lock.lock();
log.debug("获得了锁");
t1.start();
try {
    sleep(2);
} finally {
    lock.unlock();
}
4.3tryLock解决哲学家问题
class Chopstick extends ReentrantLock {
    String name;
    public Chopstick(Stringname) {
        this.name=name;   
    }
    @Override
    public String toString() {
        return"筷子{"+name+'}';    
    }
}

class Philosopher extends Thread {
	Chopstick left;
    Chopstick right;
    public Philosopher(Stringname, Chopstickleft, Chopstickright) {
	super(name);
        this.left=left;
        this.right=right;  
    }
    
    @Override
    public void run() {
        while (true) {
            // 尝试获得左手筷子
            if (left.tryLock()) {
                try {
                    // 尝试获得右手筷子
                    if (right.tryLock()) {
                        try {
                            eat();  
                        } finally {
                            right.unlock();  
                        }              
                    }         
                } finally {
                    left.unlock();     
                }      
            }    
        }  
    }
    
    private void eat() {
        log.debug("eating...");
        Sleeper.sleep(1);   
    }
}
4.4 公平锁

ReentrantLock默认是不公平的。(谁抢到就是谁的,所以是不公平的)synchronized也是不公平的。

4.5 条件变量

synchronized 中也有条件变量,就是我们讲原理时那个 waitSet 休息室,当条件不满足时进入 waitSet 等

ReentrantLock 的条件变量比 synchronized 强大之处在于,它是支持多个条件变量的,这就好比

  • synchronized 是那些不满足条件的线程都在一间休息室等消息
  • 而 ReentrantLock 支持多间休息室,有专门等烟的休息室、专门等早餐的休息室、唤醒时也是按休息室来唤醒

要点:

  • await 前需要获得锁
  • await 执行后,会释放锁,进入 conditionObject 等待
  • await 的线程被唤醒(或打断、或超时)取重新竞争 lock 锁
  • 竞争 lock 锁成功后,从 await 后继续执行
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值