Skip to content

并发编程之 ReentrantLock

基本语法

java
// 获取锁
reentrantLock.lock();
try {
    // 临界区
} finally {
    // 释放锁
    reentrantLock.unlock();
}

ReentrantLock——lock()

ReentrantLock 和 Synchronized 一样支持重入锁。

java
public class LockTest3 {
    //首先定义一把锁
    static ReentrantLock lock = new ReentrantLock();
    public static void main(String[] args) {
        lock1();
    }

    public static void lock1() {
        lock.lock();
        try {
            log.debug("执行lock1");
            //锁重入
            lock2();
        } finally {
            lock.unlock();
        }
    }

    public static void lock2() {
        lock.lock();
        try {
            log.debug("执行lock2");
        } finally {
            lock.unlock();
        }
    }
}

ReentrantLock——lockInterruptibly() 锁打断

java
public class LockTest4 {
    public static void main(String[] args) throws InterruptedException {
        ReentrantLock lock = new ReentrantLock();
        //t2首先获取锁 然后阻塞5s
        new Thread(()->{
            try {
                lock.lock();//获取锁
                log.debug("获取锁----");
                TimeUnit.SECONDS.sleep(5);
                log.debug("t2 5s 之后继续执行");
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }

        },"t2").start();

        //主要是为了让t2先拿到锁
        TimeUnit.SECONDS.sleep(1);

        // t1 加锁失败,因为锁已经被 t2 持有
        Thread t1 = new Thread(() -> {
            try {
                lock.lockInterruptibly();
                log.debug("t1 获取了锁--执行代码");
            } catch (Exception e) {
                e.printStackTrace();
                log.debug("被打断了没有获取锁");

                return;
            } finally {
                lock.unlock();
            }
        }, "t1");
        t1.start();

        //由于t1 可以被打断 故而1s之后打断t1 不在等待t2释放锁了
        try {
            TimeUnit.SECONDS.sleep(2);
            log.debug("主线程------2s后打断t1");
            t1.interrupt();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

ReentrantLock——tryLock()

java
public class LockTest5 {
    public static void main(String[] args) throws InterruptedException {
        ReentrantLock lock = new ReentrantLock();
        Thread t1 = new Thread(() -> {
            log.debug("t1启动---------");
            // if 里面也可以添加一个超时时间:lock.tryLock(2,TimeUnit.SECONDS),如果在超时时间外还没有拿到锁,直接返回 false,表示没有拿到锁
            if (!lock.tryLock()) {//进入if表示拿不到锁
                log.debug("拿不到锁,返回");
                return;
            }
            try {
                log.debug("获得了锁");
            } finally {
                lock.unlock();
            }
        }, "t1");

        //主线程先获取锁
        lock.lock();
        log.debug("主綫程获得了锁");
        t1.start();
        try {
            TimeUnit.SECONDS.sleep(3);
        } finally {
            lock.unlock();
        }
    }
}

ReentrantLock——newCondition() 多个条件解锁

java
public class LockTest6 {
    static final ReentrantLock lock = new ReentrantLock();
    static boolean isPrettyGril = false; // 女人
    static boolean isMoney = false;//工资
    //没有女人的 waitSet
    static Condition waitpg = lock.newCondition();
    // 没有钱的waitSet
    static Condition waitm = lock.newCondition();
    public static void main(String[] args) throws InterruptedException {
        new Thread(() -> {
            try {
                lock.lock();
                log.debug("有没有女人[{}]", isPrettyGril);
                while (!isPrettyGril) {
                    log.debug("没有女人!等女人");
                    try {
                        waitpg.await();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                log.debug("男女搭配干活不累;一下就写完了代码");
            }finally {
                lock.unlock();
            }
        }, "jack").start();

        new Thread(() -> {
            try {
                lock.lock();
                log.debug("有没有工资[{}]", isMoney);
                while (!isMoney) {
                    log.debug("没有工资!等发工资");
                    try {
                        waitm.await();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                log.debug("-----卧槽好多钱;一下就写完了代码");
            } finally {
                lock.unlock();
            }
        }, "rose").start();

        Thread.sleep(1000);

        new Thread(() -> {
            try {
                lock.lock();
                isMoney = true;
                log.debug("钱来哦了");
                waitm.signalAll();

                isPrettyGril=true;
                log.debug("漂亮小姐姐来了");
                waitpg.signal();
            }finally {
                lock.unlock();
            }
        }, "boss").start();
    }
}

ReentrantLock 总结

假如线程 A 和线程 B 使用同一个锁 LOCK,此时线程 A 首先获取到锁 LOCK.lock(),并且始终持有不释放。如果此时 B 要去获取锁,有四种方式:

LOCK.lock(): 此方式会始终处于等待中,即使调用 B.interrupt()也不能中断,除非线程 A 调用 LOCK.unlock()释放锁。

LOCK.lockInterruptibly(): 此方式会等待,但当调用 B.interrupt()会被中断等待,并抛出 InterruptedException 异常,否则会与 lock()一样始终处于等待中,直到线程 A 释放锁。

LOCK.tryLock(): 该处不会等待,获取不到锁并直接返回 false,去执行下面的逻辑。

LOCK.tryLock(10, TimeUnit.SECONDS):该处会在 10 秒时间内处于等待中,但当调用 B.interrupt()会被中断等待,并抛出 InterruptedException。10 秒时间内如果线程 A 释放锁,会获取到锁并返回 true,否则 10 秒过后会获取不到锁并返回 false,去执行下面的逻辑。