多线程学习二十六:ReentrantLock

ReentrantLock

相对于 synchronized 它具备如下区别特点:

  • 可中断
  • 可以设置超时时间
  • 可以设置为公平锁
  • 支持多个条件变量
  • 与 synchronized 一样,都支持可重入
    基本语法
// 获取锁
reentrantLock.lock();
try {
 // 临界区
} finally {
 // 释放锁
 reentrantLock.unlock();
}
可重入

可重入是指同一个线程如果首次获得了这把锁,那么因为它是这把锁的拥有者,因此有权利再次获取这把锁。如果是不可重入锁,那么第二次获得锁时,自己也会被锁挡住

@Slf4j(topic = "c.test32:")
public class Test32 {
    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();
        }
    }

}
输出:
17:59:11.862 [main] c.TestReentrant - execute method1 
17:59:11.865 [main] c.TestReentrant - execute method2 
17:59:11.865 [main] c.TestReentrant - execute method3 
可打断 使用lockInterruptibly
public class Test33 {
    static ReentrantLock lock = new ReentrantLock();

    public static void main(String[] args) throws InterruptedException {
        Thread t1 = new Thread(() -> {
            try {
                //lockInterruptibly 如果没有锁竞争,则成功获取到lock对象的锁
                //否则如果有竞争,则进入阻塞队列,可以被其他线程调用interrupit方法打断,停止等待
                log.debug("尝试获取锁");
                lock.lockInterruptibly();
                log.debug("获取到了锁");
            } catch (InterruptedException e) {
                e.printStackTrace();
                log.debug("没有获取到了锁");
                return;
            } finally {
                lock.unlock();
            }
        }, "t1");
        //主线程比t1先获取到了锁
        lock.lock();
        t1.start();
        //主线程休眠1秒之后打断t1的等待
        TimeUnit.SECONDS.sleep(1);
        log.debug("main线程打断t1的等待");
        t1.interrupt();
    }
}
运行结果:
21:02:23.686 c.test32: [t1] - 尝试获取锁
21:02:24.686 c.test32: [main] - main线程打断t1的等待
21:02:24.689 c.test32: [t1] - 没有获取到了锁
java.lang.InterruptedException
	at java.util.concurrent.locks.AbstractQueuedSynchronizer.doAcquireInterruptibly(AbstractQueuedSynchronizer.java:898)
	at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquireInterruptibly(AbstractQueuedSynchronizer.java:1222)
	at java.util.concurrent.locks.ReentrantLock.lockInterruptibly(ReentrantLock.java:335)
	at com.concurrent.test4.Test33.lambda$main$0(Test33.java:22)
	at com.concurrent.test4.Test33$$Lambda$1/866191240.run(Unknown Source)
	at java.lang.Thread.run(Thread.java:745)
Exception in thread "t1" java.lang.IllegalMonitorStateException
	at java.util.concurrent.locks.ReentrantLock$Sync.tryRelease(ReentrantLock.java:151)
	at java.util.concurrent.locks.AbstractQueuedSynchronizer.release(AbstractQueuedSynchronizer.java:1261)
	at java.util.concurrent.locks.ReentrantLock.unlock(ReentrantLock.java:457)
	at com.concurrent.test4.Test33.lambda$main$0(Test33.java:29)
	at com.concurrent.test4.Test33$$Lambda$1/866191240.run(Unknown Source)
	at java.lang.Thread.run(Thread.java:745)
使用lock
@Slf4j(topic = "c.test34:")
public class Test34 {
    static ReentrantLock lock = new ReentrantLock();

    public static void main(String[] args) throws InterruptedException {
        Thread t1 = new Thread(() -> {
            log.debug("尝试获取锁");
            lock.lock();
            try {
                log.debug("获取到了锁");
            } finally {
                lock.unlock();
            }
        }, "t1");
        //主线程比t1先获取到了锁
        lock.lock();
        log.debug("main获得了锁");
        t1.start();
        try {
            //主线程休眠1秒之后打断t1的等待
            TimeUnit.SECONDS.sleep(1);
            log.debug("main线程打断t1的等待");
            t1.interrupt();
        } finally {
            log.debug("main线程释放了锁");
            lock.lock();
        }
    }
}
运行结果:
21:57:52.087 c.test34: [main] - main获得了锁
21:57:52.101 c.test34: [t1] - 尝试获取锁
21:57:53.094 c.test34: [main] - main线程打断t1的等待
21:57:53.094 c.test34: [main] - main线程释放了锁

如果使用lockInterruptibly,可避免让线程死等下去,避免发生死锁.如果是不可中断模式(使用lock),那么即使使用了 interrupt 也不会让等待中断

tryLock

tryLock():尝试获取锁如果没有获得锁返回立刻失败
tryLock(long timeout, TimeUnit unit):在timeout内获取到锁返回成功,timeout外返回失败,unit是单位

使用tryLock():

@Slf4j(topic = "c.test35:")
public class Test35 {
    static ReentrantLock lock = new ReentrantLock();

    public static void main(String[] args) throws InterruptedException {
        ReentrantLock lock = new ReentrantLock();
        Thread t1 = new Thread(() -> {
            log.debug("启动...");
            if (!lock.tryLock()) { //tryLock获得锁返回true
                log.debug("获取立刻失败,返回");
                return;
            }
            try {
                log.debug("获得了锁");
            } finally {
                lock.unlock();
            }
        }, "t1");
        lock.lock();
        log.debug("获得了锁");
        t1.start();
        try {
            TimeUnit.SECONDS.sleep(1);
        } finally {
            lock.unlock();
        }

    }
}
运行结果:
22:30:30.338 c.test35: [main] - 获得了锁
22:30:30.344 c.test35: [t1] - 启动...
22:30:30.344 c.test35: [t1] - 获取立刻失败,返回

使用tryLock(long timeout, TimeUnit unit):

@Slf4j(topic = "c.test36:")
public class Test36 {
    static ReentrantLock lock = new ReentrantLock();
    public static void main(String[] args) throws InterruptedException {
        ReentrantLock lock = new ReentrantLock();
        Thread t1 = new Thread(() -> {
            log.debug("启动...");
            try {
                if (!lock.tryLock(1, TimeUnit.SECONDS)) {
                    log.debug("获取等待 1s 后失败,返回");
                    return;
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            try {
                log.debug("获得了锁");
            } finally {
                lock.unlock();
            }
        }, "t1");
        lock.lock();
        log.debug("获得了锁");
        t1.start();
        try {
            TimeUnit.SECONDS.sleep(2);
        } finally {
            lock.unlock();
        }
    }
}
公平锁

ReentrantLock 默认是不公平的,我们可以通过它的构造方法来设置他是否是公平锁

ReentrantLock lock = new ReentrantLock(false); //不公平的
ReentrantLock lock = new ReentrantLock(true); //公平的
条件变量
  • synchronized 中也有条件变量,就是我们讲原理时那个 waitSet 休息室,当条件不满足时进入 waitSet 等待
  • ReentrantLock 的条件变量比 synchronized 强大之处在于,它是支持多个条件变量的,这就好比synchronized 是那些不满足条件的线程都在一间休息室等消息
    而 ReentrantLock 支持多间休息室,有专门等烟的休息室、专门等早餐的休息室、唤醒时也是按休息室来唤醒

使用要点:

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

使用方法:

 static ReentrantLock lock = new ReentrantLock();
 //创建新的条件变量 好比一个新的休息室
 Condition condition1 = lock.newCondition();
 Condition condition2 = lock.newCondition();
 lock.lock();
 //条件不满足进入休息等待
 condition1.await();
 //其他线程condition1.signal可以叫醒休息室中其中一个线程signalAll唤醒全部
 condition1.signal();

例子:

@Slf4j(topic = "c.test38:")
public class Test38 {
    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 {
                        log.debug("没有烟休息一会");
                        waitCigaretteQueue.await();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                log.debug("等到了它的烟");
            } finally {
                lock.unlock();
            }
        }).start();
        new Thread(() -> {
            try {
                lock.lock();
                while (!hasBreakfast) {
                    try {
                        log.debug("没有早餐休息一会");
                        waitbreakfastQueue.await();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                log.debug("等到了它的早餐");
            } finally {
                lock.unlock();
            }
        }).start();
        TimeUnit.SECONDS.sleep(1);
        sendBreakfast();
        TimeUnit.SECONDS.sleep(1);
        sendCigarette();
    }

    private static void sendCigarette() {
        lock.lock();
        try {
            log.debug("送烟来了");
            hasCigrette = true;
            waitCigaretteQueue.signal();
        } finally {
            lock.unlock();
        }
    }

    private static void sendBreakfast() {
        lock.lock();
        try {
            log.debug("送早餐来了");
            hasBreakfast = true;
            waitbreakfastQueue.signal();
        } finally {
            lock.unlock();
        }
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值