学而不思则罔,思而不学则殆


【Java】深入理解ReentrantLock可重入锁之简单使用

  • ReentrantLock简介
  • ReentrantLock方法总结
  • Condition方法总结
  • ReentrantLock简单使用
  • 常用方法测试
  • 测试tryLock
  • 测试tryLock(long timeout, TimeUnit unit)
  • 测试lockInterruptibly
  • 测试Condition
  • 测试Condition.await()
  • 通过中断唤醒
  • 通过signal唤醒
  • 通过signalAll唤醒
  • 测试await(long time, TimeUnit unit)
  • 测试awaitNanos
  • 测试awaitUntil
  • 测试awaitUninterruptibly
  • 简单用法
  • 测试三个线程顺序打印1,2,3...到100 - 实现一
  • 测试三个线程顺序打印1,2,3...到100 - 实现二



ReentrantLock简介

ReentrantLock主要利用CAS+AQS队列来实现。它支持公平锁和非公平锁,两者的实现类似。

CAS:Compare and Swap,比较并交换。CAS有3个操作数:内存值V、预期值A、要修改的新值B。当且仅当预期值A和内存值V相同时,将内存值V修改为B,否则什么都不做。该操作是一个原子操作,被广泛的应用在Java的底层实现中。在Java中,CAS主要是由sun.misc.Unsafe这个类通过JNI调用CPU底层指令实现

ReentrantLock方法总结

方法

说明

lock

等待线程获取锁,不能被中断

lockInterruptibly

等待线程获取锁,接收中断信号

tryLock

方法是有返回值的,它表示用来尝试获取锁,如果获取成功,则返回true,如果获取失败(即锁已被其他线程获取),则返回false,这个方法无论如何都会立即返回

tryLock(long timeout, TimeUnit unit)

尝试获取锁,在指定时间内获取成功会返回,超过时间返回false,可以被中断

getHoldCount

获取当前线程持有锁的次数

getQueueLength

获取等待锁的线程个数

isFair

锁是否公平

hasQueuedThreads

查询是否有线程正在等待获取此锁

hasQueuedThread(Thread)

查询是指定线程是否正在等待获取此锁

isHeldByCurrentThread

查询当前线程是否持有此锁

isLocked

查询此锁是否由任何线程持有

newCondition

创建Condition

Condition方法总结

Condition可以定向唤醒线程

方法

说明

await

等待某个条件唤醒或者中断

await (long,TimeUnit)

等待xignal信号或者中断或者超时

awaitNanos

等待signal信号或者中断或者超时

awaitUntil

使当前线程等待,直到收到信号或中断,或指定的截止日期过期。

awaitUninterruptibly

等待信号,不能中断

signal

发送一个信号

signalAll

发送该条件的所有信号

ReentrantLock简单使用

常用方法测试

private static void testLock() {
        sDiyThread1.start();
        sDiyThread2.start();
        sDiyThread3.start();
        try {
            TimeUnit.SECONDS.sleep(5);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        showMsg();
    }


    static ReentrantLock sReentrantLock = new ReentrantLock();
    static DiyThread sDiyThread1 = new DiyThread(sReentrantLock);
    static DiyThread sDiyThread2 = new DiyThread(sReentrantLock);
    static DiyThread sDiyThread3 = new DiyThread(sReentrantLock);

    static void showMsg() {
        System.out.println(Thread.currentThread() + " 获取当前线程持有锁的次数:" + sReentrantLock.getHoldCount());
        System.out.println(Thread.currentThread() + " 获取等待锁的线程个数:" + sReentrantLock.getQueueLength());
        System.out.println(Thread.currentThread() + " 锁是否公平:" + sReentrantLock.isFair());
        System.out.println(Thread.currentThread() + " 查询是否有线程正在等待获取此锁:" + sReentrantLock.hasQueuedThreads());
        System.out.println(Thread.currentThread() + " 查询是指定线程" + sDiyThread1.getName() + "正在等待获取此锁:" + sReentrantLock.hasQueuedThread(sDiyThread1));
        System.out.println(Thread.currentThread() + " 查询是指定线程" + sDiyThread2.getName() + "正在等待获取此锁:" + sReentrantLock.hasQueuedThread(sDiyThread2));
        System.out.println(Thread.currentThread() + " 查询是指定线程" + sDiyThread3.getName() + "正在等待获取此锁:" + sReentrantLock.hasQueuedThread(sDiyThread3));
        System.out.println(Thread.currentThread() + " 查询当前线程是否持有此锁:" + sReentrantLock.isHeldByCurrentThread());
        System.out.println(Thread.currentThread() + " 查询此锁是否由任何线程持有:" + sReentrantLock.isLocked());
    }

    static class DiyThread extends Thread {
        ReentrantLock mReentrantLock;

        public DiyThread(ReentrantLock reentrantLock) {
            this.mReentrantLock = reentrantLock;
        }

        @Override
        public void run() {
            try {
                //1.获得锁
                mReentrantLock.lock();
                mReentrantLock.lock();
                TimeUnit.SECONDS.sleep(1);
                showMsg();
                //睡眠一分钟
                TimeUnit.MINUTES.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                //2.释放重入锁
                mReentrantLock.unlock();
                mReentrantLock.unlock();
            }
        }
    }

新建三个线程,其中一个线程获取锁后睡眠1分钟,这个时候打印一下当前的锁获取情况信息:

信息如下:

Thread[Thread-0,5,main] 获取当前线程持有锁的次数:2
Thread[Thread-0,5,main] 获取等待锁的线程个数:2
Thread[Thread-0,5,main] 锁是否公平:false
Thread[Thread-0,5,main] 查询是否有线程正在等待获取此锁:true
Thread[Thread-0,5,main] 查询是指定线程Thread-0正在等待获取此锁:false
Thread[Thread-0,5,main] 查询是指定线程Thread-1正在等待获取此锁:true
Thread[Thread-0,5,main] 查询是指定线程Thread-2正在等待获取此锁:true
Thread[Thread-0,5,main] 查询当前线程是否持有此锁:true
Thread[Thread-0,5,main] 查询此锁是否由任何线程持有:true

Thread[main,5,main] 获取当前线程持有锁的次数:0
Thread[main,5,main] 获取等待锁的线程个数:2
Thread[main,5,main] 锁是否公平:false
Thread[main,5,main] 查询是否有线程正在等待获取此锁:true
Thread[main,5,main] 查询是指定线程Thread-0正在等待获取此锁:false
Thread[main,5,main] 查询是指定线程Thread-1正在等待获取此锁:true
Thread[main,5,main] 查询是指定线程Thread-2正在等待获取此锁:true
Thread[main,5,main] 查询当前线程是否持有此锁:false
Thread[main,5,main] 查询此锁是否由任何线程持有:true

测试tryLock

tryLock()方法是有返回值的,它表示用来尝试获取锁,如果获取成功,则返回true,如果获取失败(即锁已被其他线程获取),则返回false,这个方法无论如何都会立即返回。在拿不到锁时不会一直在那等待。

static class TestTryLockThread extends Thread {
        ReentrantLock mReentrantLock;

        public TestTryLockThread(ReentrantLock reentrantLock) {
            this.mReentrantLock = reentrantLock;
        }

        @Override
        public void run() {
            //1.尝试获得锁
            System.out.println(System.currentTimeMillis() + " " + Thread.currentThread() + " tryLock");
            if (mReentrantLock.tryLock()) {
                System.out.println(Thread.currentThread() + "获取到锁!");
            } else {
                System.out.println(Thread.currentThread() + "获取不到锁!");
                return;
            }
            try {
                boolean isLocked = mReentrantLock.isHeldByCurrentThread();
                System.out.println(System.currentTimeMillis() + " " + Thread.currentThread() + " isLocked:" + isLocked);
                if (isLocked) {
                    TimeUnit.MINUTES.sleep(1);
                }
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                //2.释放重入锁
                System.out.println(System.currentTimeMillis() + " " + Thread.currentThread() + " unlock");
                mReentrantLock.unlock();
            }
        }
    }

测试结果如下:

1603153131202 Thread[Thread-3,5,main] tryLock
Thread[Thread-3,5,main]获取到锁!
1603153131203 Thread[Thread-3,5,main] isLocked:true
1603153131203 Thread[Thread-4,5,main] tryLock
Thread[Thread-4,5,main]获取不到锁!
1603153131203 Thread[Thread-5,5,main] tryLock
Thread[Thread-5,5,main]获取不到锁!
1603153191204 Thread[Thread-3,5,main] unlock

线程【Thread-3】获取到了锁,然后休眠1分钟,【Thread-4】【Thread-5】没有获取到锁,但是呢,立马就返回了。

测试tryLock(long timeout, TimeUnit unit)

这个方法去限定了一个尝试获取锁的时间。
—获取锁成功则返回true;
—当失败是分为两种情况:
在参数范围内,则不会立即返回值,会等待一段时间,这个时间就是传入的具体参数值,在这个时间内获取锁成功,则依旧返回true;当过了参数范围后,还是获取锁失败,则立即返回false。
比如如下的测试demo:

//指定时间内是否获取到锁
    static class TestTryLockTimeThread extends Thread {
        ReentrantLock mReentrantLock;
        int seconds;

        public TestTryLockTimeThread(ReentrantLock reentrantLock, int seconds) {
            this.mReentrantLock = reentrantLock;
            this.seconds = seconds;
        }

        @Override
        public void run() {
            //1.尝试获得锁
            System.out.println(System.currentTimeMillis() + " " + Thread.currentThread() + " tryLock 尝试获取锁的时间为:" + this.seconds);

            try {
                if (mReentrantLock.tryLock(seconds, TimeUnit.SECONDS)) {
                    System.out.println(System.currentTimeMillis() + " " + Thread.currentThread() + "获取到锁!");
                    try {
                        boolean isHeldByCurrentThread = mReentrantLock.isHeldByCurrentThread();
                        System.out.println(System.currentTimeMillis() + " " + Thread.currentThread() + " isHeldByCurrentThread:" + isHeldByCurrentThread);
                        if (isHeldByCurrentThread) {
                            TimeUnit.SECONDS.sleep(10); //拿到锁后休眠10s
                        }
                    } catch (Exception e) {
                        e.printStackTrace();
                    } finally {
                        //2.释放重入锁
                        System.out.println(System.currentTimeMillis() + " " + Thread.currentThread() + " unlock");
                        mReentrantLock.unlock();
                    }
                } else {
                    System.out.println(System.currentTimeMillis() + " " + Thread.currentThread() + "获取不到锁!");
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
    private static void testTryLockLT() throws InterruptedException {
        ReentrantLock reentrantLock = new ReentrantLock();
        TestTryLockTimeThread timeThread1 = new TestTryLockTimeThread(reentrantLock, 0);
        TestTryLockTimeThread timeThread2 = new TestTryLockTimeThread(reentrantLock, 5); //5秒内没有拿到锁就返回
        TestTryLockTimeThread timeThread3 = new TestTryLockTimeThread(reentrantLock, 15);//15秒内没有拿到锁就返回
        timeThread1.start();
        TimeUnit.SECONDS.sleep(1); //休眠一秒,确保timeThread1拿到锁
        timeThread2.start();
        timeThread3.start();
    }

新建三个线程获取重入锁,【timeThread2】等待5秒还没有获取成功就返回,【timeThread3】等待15秒还没有获取成功就返回。而其中【timeThread1】会先获取到锁后休眠10s,那么就会导致【timeThread2】获取失败,【timeThread3】获取成功。log信息如下:

1603154345942 Thread[Thread-3,5,main] tryLock 尝试获取锁的时间为:0
1603154345943 Thread[Thread-3,5,main]获取到锁!
1603154345943 Thread[Thread-3,5,main] isHeldByCurrentThread:true
1603154346943 Thread[Thread-4,5,main] tryLock 尝试获取锁的时间为:5
1603154346944 Thread[Thread-5,5,main] tryLock 尝试获取锁的时间为:15
1603154351943 Thread[Thread-4,5,main]获取不到锁!
1603154355944 Thread[Thread-3,5,main] unlock
1603154355945 Thread[Thread-5,5,main]获取到锁!
1603154355945 Thread[Thread-5,5,main] isHeldByCurrentThread:true
1603154365946 Thread[Thread-5,5,main] unlock

可以看到【Thread-4】等待了5秒,在【46 - 51】的时候,就直接返回了。而【Thread-5】等待15秒,在【1603154355945】的是否获取到了锁。

测试lockInterruptibly

获取锁的时候可中断

static class TestLockInterruptiblyThread extends Thread {
        ReentrantLock mReentrantLock;

        public TestLockInterruptiblyThread(ReentrantLock reentrantLock) {
            this.mReentrantLock = reentrantLock;
        }

        @Override
        public void run() {
            try {
                System.out.println(System.currentTimeMillis() + " " + Thread.currentThread() + " lockInterruptibly" );
                //1.尝试获得锁
                mReentrantLock.lockInterruptibly();
                System.out.println(System.currentTimeMillis() + " " + Thread.currentThread() + "获取到锁!");
                //获取锁后休眠10s
                TimeUnit.SECONDS.sleep(20);
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                if (mReentrantLock.isHeldByCurrentThread()) {
                    //释放锁
                    System.out.println(System.currentTimeMillis() + " " + Thread.currentThread() + "释放锁!");
                    mReentrantLock.unlock();
                }
            }
        }
    }

    private static void testLockInterruptibly() {
        ReentrantLock reentrantLock = new ReentrantLock();
        TestLockInterruptiblyThread thread1 = new TestLockInterruptiblyThread(reentrantLock);
        TestLockInterruptiblyThread thread2 = new TestLockInterruptiblyThread(reentrantLock);
        TestLockInterruptiblyThread thread3 = new TestLockInterruptiblyThread(reentrantLock);
        thread1.setName("zy-test1");
        thread2.setName("zy-test2");
        thread3.setName("zy-test3");
        thread1.start();
        try {
            //休眠5s确保thread1获取锁
            TimeUnit.SECONDS.sleep(5);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        thread2.start();
        thread3.start();
        try {
            //休眠10s
            TimeUnit.SECONDS.sleep(10);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        //发送中断信号
        System.out.println(System.currentTimeMillis() + " " + Thread.currentThread() + " thread2 thread3 interrupt");
        thread2.interrupt();
        thread3.interrupt();
    }

测试主要是新建三个线程:

第一个线程获取锁休眠20s
第二个线程和第三个线程5秒后启动,确保第一个线程获取锁
在这10秒中通过jstack打印一下线程状态信息
启动10秒后发送中断信号,第二个和第三个线程

log信息如下:

1603237475627 Thread[zy-test1,5,main] lockInterruptibly
1603237475627 Thread[zy-test1,5,main]获取到锁!
1603237480628 Thread[zy-test2,5,main] lockInterruptibly
1603237480628 Thread[zy-test3,5,main] lockInterruptibly
1603237490628 Thread[main,5,main] thread2 thread3 interrupt
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.lock.ReentrantLockTest$TestLockInterruptiblyThread.run(ReentrantLockTest.java:38)
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.lock.ReentrantLockTest$TestLockInterruptiblyThread.run(ReentrantLockTest.java:38)
1603237495627 Thread[zy-test1,5,main]释放锁!

可以看到线程【zy-test1】正常休眠结束释放锁,线程【zy-test2】【zy-test3】被中断了但我们手动发送中断信号的时候,打印出了中断的异常堆栈。

其中在线程【zy-test2】【zy-test3】等待获取锁的线程状态如下:

$ jstack.exe 6056
2020-10-21 07:44:48
Full thread dump Java HotSpot(TM) 64-Bit Server VM (25.181-b13 mixed mode):

"zy-test3" #16 prio=5 os_prio=0 tid=0x0000000022864000 nid=0x5e8 waiting on condition [0x000000002350e000]
   java.lang.Thread.State: WAITING (parking)
        at sun.misc.Unsafe.park(Native Method)
        - parking to wait for  <0x00000007406ab408> (a java.util.concurrent.locks.ReentrantLock$NonfairSync)
        at java.util.concurrent.locks.LockSupport.park(LockSupport.java:175)
        at java.util.concurrent.locks.AbstractQueuedSynchronizer.parkAndCheckInterrupt(AbstractQueuedSynchronizer.java:836)
        at java.util.concurrent.locks.AbstractQueuedSynchronizer.doAcquireInterruptibly(AbstractQueuedSynchronizer.java:897)
        at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquireInterruptibly(AbstractQueuedSynchronizer.java:1222)
        at java.util.concurrent.locks.ReentrantLock.lockInterruptibly(ReentrantLock.java:335)
        at com.lock.ReentrantLockTest$TestLockInterruptiblyThread.run(ReentrantLockTest.java:38)

"zy-test2" #15 prio=5 os_prio=0 tid=0x0000000022860800 nid=0x964 waiting on condition [0x000000002340f000]
   java.lang.Thread.State: WAITING (parking)
        at sun.misc.Unsafe.park(Native Method)
        - parking to wait for  <0x00000007406ab408> (a java.util.concurrent.locks.ReentrantLock$NonfairSync)
        at java.util.concurrent.locks.LockSupport.park(LockSupport.java:175)
        at java.util.concurrent.locks.AbstractQueuedSynchronizer.parkAndCheckInterrupt(AbstractQueuedSynchronizer.java:836)
        at java.util.concurrent.locks.AbstractQueuedSynchronizer.doAcquireInterruptibly(AbstractQueuedSynchronizer.java:897)
        at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquireInterruptibly(AbstractQueuedSynchronizer.java:1222)
        at java.util.concurrent.locks.ReentrantLock.lockInterruptibly(ReentrantLock.java:335)
        at com.lock.ReentrantLockTest$TestLockInterruptiblyThread.run(ReentrantLockTest.java:38)

"zy-test1" #14 prio=5 os_prio=0 tid=0x0000000022803000 nid=0x33d8 waiting on condition [0x000000002330e000]
   java.lang.Thread.State: TIMED_WAITING (sleeping)
        at java.lang.Thread.sleep(Native Method)
        at java.lang.Thread.sleep(Thread.java:340)
        at java.util.concurrent.TimeUnit.sleep(TimeUnit.java:386)
        at com.lock.ReentrantLockTest$TestLockInterruptiblyThread.run(ReentrantLockTest.java:41)

此时三个线程的状态分别是:

“zy-test1” TIMED_WAITING (sleeping)
“zy-test2” WAITING (parking)
“zy-test3” WAITING (parking)

知道此时三个线程都是出于挂起的状态,但是挂起的方式不同,【zy-test1】是通过sleep方式,进入TIMED_WAITING 状态,可以通过中断信号打断或者时间到了自动唤醒。【zy-test2】和【zy-test3】则是通过Unsafe.park方法让线程处于WAITING 状态。

测试Condition

测试Condition.await()

static class TestConditionThread extends Thread {
        ReentrantLock mReentrantLock;
        Condition mCondition;

        public TestConditionThread(ReentrantLock reentrantLock, Condition condition) {
            this.mReentrantLock = reentrantLock;
            this.mCondition = condition;
        }

        @Override
        public void run() {
            try {
                System.out.println(System.currentTimeMillis() + " " + Thread.currentThread() + " lock");
                //1.尝试获得锁
                mReentrantLock.lock();
                System.out.println(System.currentTimeMillis() + " " + Thread.currentThread() + "获取到锁");
                System.out.println(System.currentTimeMillis() + " " + Thread.currentThread() + "await start");
                mCondition.await();
                System.out.println(System.currentTimeMillis() + " " + Thread.currentThread() + "await end");
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                if (mReentrantLock.isHeldByCurrentThread()) {
                    //释放锁
                    System.out.println(System.currentTimeMillis() + " " + Thread.currentThread() + "释放锁");
                    mReentrantLock.unlock();
                }
            }
        }
    }

    //简单使用
    private static void testNewCondition1() {
        ReentrantLock reentrantLock = new ReentrantLock();
        Condition condition = reentrantLock.newCondition();
        TestConditionThread thread = new TestConditionThread(reentrantLock, condition);
        thread.setName("test_condition_await");
        thread.start();
    }

新建一个线程,获取锁后调用await()进入等待状态。线程状态如下:

$ jstack.exe 1220
2020-10-21 08:19:52
Full thread dump Java HotSpot(TM) 64-Bit Server VM (25.181-b13 mixed mode):

"DestroyJavaVM" #15 prio=5 os_prio=0 tid=0x00000000025ce800 nid=0x1d20 waiting on condition [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"test_condition_await" #14 prio=5 os_prio=0 tid=0x0000000021ef1800 nid=0x3eb4 waiting on condition [0x0000000022a6f000]
   java.lang.Thread.State: WAITING (parking)
        at sun.misc.Unsafe.park(Native Method)
        - parking to wait for  <0x00000007406ad228> (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject)
        at java.util.concurrent.locks.LockSupport.park(LockSupport.java:175)
        at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(AbstractQueuedSynchronizer.java:2039)
        at com.lock.ReentrantLockTest$TestConditionThread.run(ReentrantLockTest.java:45)
通过中断唤醒
TimeUnit.SECONDS.sleep(2);
        System.out.println(System.currentTimeMillis() + " " + Thread.currentThread() + " interrupt");
        thread.interrupt();
1603239799798 Thread[test_condition_await,5,main] lock
1603239799798 Thread[test_condition_await,5,main]获取到锁
1603239799798 Thread[test_condition_await,5,main]await start
1603239801799 Thread[main,5,main] interrupt
1603239801802 Thread[test_condition_await,5,main]释放锁
java.lang.InterruptedException
	at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.reportInterruptAfterWait(AbstractQueuedSynchronizer.java:2014)
	at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(AbstractQueuedSynchronizer.java:2048)
	at com.lock.ReentrantLockTest$TestConditionThread.run(ReentrantLockTest.java:47)

发送中断信号后,线程被打断,唤醒线程。

通过signal唤醒

新建一个线程用来测试发送signal信号

static class TestSignalThread extends Thread {
        ReentrantLock mReentrantLock;
        Condition mCondition;

        public TestSignalThread(ReentrantLock reentrantLock, Condition condition) {
            this.mReentrantLock = reentrantLock;
            this.mCondition = condition;
        }

        @Override
        public void run() {
            try {
                System.out.println(System.currentTimeMillis() + " " + Thread.currentThread() + " lock");
                //1.尝试获得锁
                mReentrantLock.lock();
                System.out.println(System.currentTimeMillis() + " " + Thread.currentThread() + "获取到锁");
                System.out.println(System.currentTimeMillis() + " " + Thread.currentThread() + " 发送signal信号");
                mCondition.signal();
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                if (mReentrantLock.isHeldByCurrentThread()) {
                    //释放锁
                    System.out.println(System.currentTimeMillis() + " " + Thread.currentThread() + "释放锁");
                    mReentrantLock.unlock();
                }
            }
        }
    }
TimeUnit.SECONDS.sleep(2);
        TestSignalThread signalThread = new TestSignalThread(reentrantLock, condition);
        signalThread.setName("test_signal");
        signalThread.start();

测试结果如下:

1603240076885 Thread[test_condition_await,5,main] lock
1603240076886 Thread[test_condition_await,5,main]获取到锁
1603240076886 Thread[test_condition_await,5,main]await start

1603240078883 Thread[test_signal,5,main] lock
1603240078883 Thread[test_signal,5,main]获取到锁
1603240078883 Thread[test_signal,5,main] 发送signal信号
1603240078883 Thread[test_signal,5,main]释放锁

1603240078883 Thread[test_condition_await,5,main]await end
1603240078883 Thread[test_condition_await,5,main]释放锁

【test_signal】发送信号后且释放锁后,【test_condition_await】被唤醒。

通过signalAll唤醒
static class TestSignalThread extends Thread {
        ReentrantLock mReentrantLock;
        Condition mCondition;

        public TestSignalThread(ReentrantLock reentrantLock, Condition condition) {
            this.mReentrantLock = reentrantLock;
            this.mCondition = condition;
        }

        @Override
        public void run() {
            try {
                System.out.println(System.currentTimeMillis() + " " + Thread.currentThread() + " lock");
                //1.尝试获得锁
                mReentrantLock.lock();
                System.out.println(System.currentTimeMillis() + " " + Thread.currentThread() + "获取到锁");
                System.out.println(System.currentTimeMillis() + " " + Thread.currentThread() + " 发送signalAll信号");
                mCondition.signalAll();
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                if (mReentrantLock.isHeldByCurrentThread()) {
                    //释放锁
                    System.out.println(System.currentTimeMillis() + " " + Thread.currentThread() + "释放锁");
                    mReentrantLock.unlock();
                }
            }
        }
    }
1603240230998 Thread[test_condition_await,5,main] lock
1603240230998 Thread[test_condition_await,5,main]获取到锁
1603240230998 Thread[test_condition_await,5,main]await start
1603240233002 Thread[test_signal,5,main] lock
1603240233002 Thread[test_signal,5,main]获取到锁
1603240233002 Thread[test_signal,5,main] 发送signalAll信号
1603240233002 Thread[test_signal,5,main]释放锁
1603240233003 Thread[test_condition_await,5,main]await end
1603240233003 Thread[test_condition_await,5,main]释放锁

测试await(long time, TimeUnit unit)

超时等待,超过时间没有唤醒会自动结束

static class TestConditionThread extends Thread {
        ReentrantLock mReentrantLock;
        Condition mCondition;

        public TestConditionThread(ReentrantLock reentrantLock, Condition condition) {
            this.mReentrantLock = reentrantLock;
            this.mCondition = condition;
        }

        @Override
        public void run() {
            try {
                System.out.println(System.currentTimeMillis() + " " + Thread.currentThread() + " lock");
                //1.尝试获得锁
                mReentrantLock.lock();
                System.out.println(System.currentTimeMillis() + " " + Thread.currentThread() + "获取到锁");
                System.out.println(System.currentTimeMillis() + " " + Thread.currentThread() + "await(10, TimeUnit.SECONDS) start");
                mCondition.await(10, TimeUnit.SECONDS);
                System.out.println(System.currentTimeMillis() + " " + Thread.currentThread() + "await(10, TimeUnit.SECONDS) end");
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                if (mReentrantLock.isHeldByCurrentThread()) {
                    //释放锁
                    System.out.println(System.currentTimeMillis() + " " + Thread.currentThread() + "释放锁");
                    mReentrantLock.unlock();
                }
            }
        }
    }
private static void testNewCondition1() throws InterruptedException {
        ReentrantLock reentrantLock = new ReentrantLock();
        Condition condition = reentrantLock.newCondition();
        TestConditionThread thread = new TestConditionThread(reentrantLock, condition);
        thread.setName("test_condition_await");
        thread.start();
    }

等待10秒后,没有收到中断信号或者signal信号也会自动唤醒:

1603241143625 Thread[test_condition_await,5,main]获取到锁
1603241143625 Thread[test_condition_await,5,main]await(10, TimeUnit.SECONDS) start
1603241153627 Thread[test_condition_await,5,main]await(10, TimeUnit.SECONDS) end
1603241153627 Thread[test_condition_await,5,main]释放锁

测试awaitNanos

static class TestConditionThread extends Thread {
        ReentrantLock mReentrantLock;
        Condition mCondition;

        public TestConditionThread(ReentrantLock reentrantLock, Condition condition) {
            this.mReentrantLock = reentrantLock;
            this.mCondition = condition;
        }

        @Override
        public void run() {
            try {
                System.out.println(System.currentTimeMillis() + " " + Thread.currentThread() + " lock");
                //1.尝试获得锁
                mReentrantLock.lock();
                System.out.println(System.currentTimeMillis() + " " + Thread.currentThread() + "获取到锁");
                System.out.println(System.currentTimeMillis() + " " + Thread.currentThread() + "awaitNanos start");
                long na = TimeUnit.NANOSECONDS.convert(5, TimeUnit.SECONDS);
                System.out.println(System.currentTimeMillis() + " " + Thread.currentThread() + " na:"+na);
                mCondition.awaitNanos(na);
                System.out.println(System.currentTimeMillis() + " " + Thread.currentThread() + "awaitNanos end");
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                if (mReentrantLock.isHeldByCurrentThread()) {
                    //释放锁
                    System.out.println(System.currentTimeMillis() + " " + Thread.currentThread() + "释放锁");
                    mReentrantLock.unlock();
                }
            }
        }
    }

测试结果:
等待5秒后,没有收到中断信号或者signal信号也会自动唤醒:

1603241437769 Thread[test_condition_await,5,main] lock
1603241437770 Thread[test_condition_await,5,main]获取到锁
1603241437770 Thread[test_condition_await,5,main]awaitNanos start
1603241437770 Thread[test_condition_await,5,main] na:5000000000
1603241442771 Thread[test_condition_await,5,main]awaitNanos end
1603241442771 Thread[test_condition_await,5,main]释放锁

测试awaitUntil

使当前线程等待,直到收到信号或中断,或指定的截止日期过期。

static class TestConditionThread extends Thread {
        ReentrantLock mReentrantLock;
        Condition mCondition;

        public TestConditionThread(ReentrantLock reentrantLock, Condition condition) {
            this.mReentrantLock = reentrantLock;
            this.mCondition = condition;
        }

        @Override
        public void run() {
            try {
                System.out.println(System.currentTimeMillis() + " " + Thread.currentThread() + " lock");
                //1.尝试获得锁
                mReentrantLock.lock();
                System.out.println(System.currentTimeMillis() + " " + Thread.currentThread() + "获取到锁");
                System.out.println(System.currentTimeMillis() + " " + Thread.currentThread() + "awaitUntil start");
                mCondition.awaitUntil(new Date(System.currentTimeMillis() + 1000 * 5));
                System.out.println(System.currentTimeMillis() + " " + Thread.currentThread() + "awaitUntil end");
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                if (mReentrantLock.isHeldByCurrentThread()) {
                    //释放锁
                    System.out.println(System.currentTimeMillis() + " " + Thread.currentThread() + "释放锁");
                    mReentrantLock.unlock();
                }
            }
        }
    }

测试结果,测试截止日期为当前时间+5秒:

1603283671821 Thread[test_condition_await,5,main] lock
1603283671821 Thread[test_condition_await,5,main]获取到锁
1603283671821 Thread[test_condition_await,5,main]awaitUntil start
1603283676832 Thread[test_condition_await,5,main]awaitUntil end
1603283676832 Thread[test_condition_await,5,main]释放锁

测试awaitUninterruptibly

导致当前线程等待,直到收到信号。意思中断不了

static class TestConditionThread extends Thread {
        ReentrantLock mReentrantLock;
        Condition mCondition;

        public TestConditionThread(ReentrantLock reentrantLock, Condition condition) {
            this.mReentrantLock = reentrantLock;
            this.mCondition = condition;
        }

        @Override
        public void run() {
            try {
                System.out.println(System.currentTimeMillis() + " " + Thread.currentThread() + " lock");
                //1.尝试获得锁
                mReentrantLock.lock();
                System.out.println(System.currentTimeMillis() + " " + Thread.currentThread() + "获取到锁");
                System.out.println(System.currentTimeMillis() + " " + Thread.currentThread() + "awaitUninterruptibly start");
                mCondition.awaitUninterruptibly();
                System.out.println(System.currentTimeMillis() + " " + Thread.currentThread() + "awaitUninterruptibly end");
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                if (mReentrantLock.isHeldByCurrentThread()) {
                    //释放锁
                    System.out.println(System.currentTimeMillis() + " " + Thread.currentThread() + "释放锁");
                    mReentrantLock.unlock();
                }
            }
        }
    }
    //测试发送signal的线程
    static class TestSignalThread extends Thread {
        ReentrantLock mReentrantLock;
        Condition mCondition;

        public TestSignalThread(ReentrantLock reentrantLock, Condition condition) {
            this.mReentrantLock = reentrantLock;
            this.mCondition = condition;
        }

        @Override
        public void run() {
            try {
                System.out.println(System.currentTimeMillis() + " " + Thread.currentThread() + " lock");
                //1.尝试获得锁
                mReentrantLock.lock();
                System.out.println(System.currentTimeMillis() + " " + Thread.currentThread() + "获取到锁后休眠2秒");
                TimeUnit.SECONDS.sleep(2);
                System.out.println(System.currentTimeMillis() + " " + Thread.currentThread() + " 发送signalAll信号");
                mCondition.signalAll();
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                if (mReentrantLock.isHeldByCurrentThread()) {
                    //释放锁
                    System.out.println(System.currentTimeMillis() + " " + Thread.currentThread() + "释放锁");
                    mReentrantLock.unlock();
                }
            }
        }
    }
private static void testNewCondition1() throws InterruptedException {
        ReentrantLock reentrantLock = new ReentrantLock();
        Condition condition = reentrantLock.newCondition();
        TestConditionThread thread = new TestConditionThread(reentrantLock, condition);
        thread.setName("test_condition_await");
        thread.start();

        TimeUnit.SECONDS.sleep(2);
        System.out.println(System.currentTimeMillis() + " " + Thread.currentThread() + " interrupt");
        thread.interrupt();
        TestSignalThread signalThread = new TestSignalThread(reentrantLock, condition);
        signalThread.setName("test_signal");
        signalThread.start();
    }
1603284043331 Thread[test_condition_await,5,main] lock
1603284043331 Thread[test_condition_await,5,main]获取到锁
1603284043331 Thread[test_condition_await,5,main]awaitUninterruptibly start
1603284045332 Thread[main,5,main] interrupt  
1603284045340 Thread[test_signal,5,main] lock
1603284045340 Thread[test_signal,5,main]获取到锁后休眠2秒
1603284047347 Thread[test_signal,5,main] 发送signalAll信号
1603284047347 Thread[test_signal,5,main]释放锁
1603284047347 Thread[test_condition_await,5,main]awaitUninterruptibly end
1603284047347 Thread[test_condition_await,5,main]释放锁

发送中断信号没有打断【test_condition_await】线程的等待。只有发送信号才能唤醒【test_condition_await】线程。

简单用法

测试三个线程顺序打印1,2,3…到100 - 实现一

通过公共状态类维护

//三个线程,顺序执行,打印100
    private static void test1() {
        ReentrantLock reentrantLock = new ReentrantLock();
        Bean bean = new Bean(100, new AtomicInteger(0));
        TestOneRunnable testOneRunnable1 = new TestOneRunnable(reentrantLock, bean, 0);
        TestOneRunnable testOneRunnable2 = new TestOneRunnable(reentrantLock, bean, 1);
        TestOneRunnable testOneRunnable3 = new TestOneRunnable(reentrantLock, bean, 2);
        ExecutorService executorService = Executors.newCachedThreadPool();
        executorService.submit(testOneRunnable1);
        executorService.submit(testOneRunnable2);
        executorService.submit(testOneRunnable3);
        executorService.shutdown();
    }
    
    static class Bean {
        int index;
        volatile AtomicInteger status;
        int num;

        public Bean(int num, AtomicInteger status) {
            this.num = num;
            this.status = status;
        }
    }

    static class TestOneRunnable implements Runnable {
        
        ReentrantLock mReentrantLock;
        Bean mBean;
        int mFlag = -1;

        public TestOneRunnable(ReentrantLock reentrantLock, Bean bean, int flag) {
            this.mReentrantLock = reentrantLock;
            this.mBean = bean;
            this.mFlag = flag;
        }

        @Override
        public void run() {
            //System.out.println(Thread.currentThread() + " start");
            while (mBean.index < mBean.num) {
                try {
                    //1.获得锁
                    mReentrantLock.lock();
                    if (mBean.index >= mBean.num) {
                        return;
                    }
                    //改变状态
                    if (mFlag == (mBean.status.intValue() % 3)) {
                        System.out.println(Thread.currentThread() + " " + mBean.index++);
                        mBean.status.incrementAndGet(); //状态+1
                    }
                } finally {
                    //2.释放重入锁
                    mReentrantLock.unlock();
                }
            }
            //System.out.println(Thread.currentThread() + " end");
        }
    }

测试结果:

Thread[pool-1-thread-1,5,main] 0
Thread[pool-1-thread-2,5,main] 1
Thread[pool-1-thread-3,5,main] 2
Thread[pool-1-thread-1,5,main] 3
Thread[pool-1-thread-2,5,main] 4
Thread[pool-1-thread-3,5,main] 5
...
Thread[pool-1-thread-1,5,main] 93
Thread[pool-1-thread-2,5,main] 94
Thread[pool-1-thread-3,5,main] 95
Thread[pool-1-thread-1,5,main] 96
Thread[pool-1-thread-2,5,main] 97
Thread[pool-1-thread-3,5,main] 98
Thread[pool-1-thread-1,5,main] 99

三个线程循环打印数字到100,每个线程获得锁过后,判断是否该自己打印,如果该自己打印,打印数字并改变数组和状态后,在释放锁,在尝试获取锁,等待其他线程更改状态满足条件。

测试三个线程顺序打印1,2,3…到100 - 实现二

通过Condition实现

static class TestPrintThread extends Thread {
        ReentrantLock mReentrantLock;
        PrintBean mPrintBean;
        Condition mConditionCurr; //当前线程的条件
        Condition mConditionNext; //下一个线程的条件

        public TestPrintThread(ReentrantLock reentrantLock, PrintBean printBean, Condition conditionCurr, Condition conditionNext) {
            this.mReentrantLock = reentrantLock;
            this.mPrintBean = printBean;
            this.mConditionCurr = conditionCurr;
            this.mConditionNext = conditionNext;
        }

        @Override
        public void run() {
            //System.out.println(Thread.currentThread() + " start");
            while (mPrintBean.currentIndex < mPrintBean.max) {
                try {
                    //1.获得锁
                    mReentrantLock.lock();
                    if (mPrintBean.currentIndex >= mPrintBean.max) {
                        System.out.println(Thread.currentThread() + " end1");
                        mConditionNext.signal();
                        return;
                    }
                    System.out.println(Thread.currentThread() + " " + mPrintBean.currentIndex++);
                    //发送信号,唤醒下一个线程
                    mConditionNext.signal();
                    //当前线程等待
                    //System.out.println(Thread.currentThread() + " wait");
                    mConditionCurr.await();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    //发送信号,唤醒下一个线程,防止下一次循环的时候直接退出,没有发送信号
                    mConditionNext.signal();
                    //释放重入锁
                    mReentrantLock.unlock();
                }
            }
            System.out.println(Thread.currentThread() + " end2");
        }
    }

    //三个线程,顺序执行,打印100
    private static void test100() throws InterruptedException {
        ReentrantLock reentrantLock = new ReentrantLock();
        Condition condition1 = reentrantLock.newCondition();
        Condition condition2 = reentrantLock.newCondition();
        Condition condition3 = reentrantLock.newCondition();
        PrintBean printBean = new PrintBean(100, 0);
        TestPrintThread printRunnable1 = new TestPrintThread(reentrantLock, printBean, condition1, condition2);
        TestPrintThread printRunnable2 = new TestPrintThread(reentrantLock, printBean, condition2, condition3);
        TestPrintThread printRunnable3 = new TestPrintThread(reentrantLock, printBean, condition3, condition1);

        printRunnable1.start();
        printRunnable2.start();
        printRunnable3.start();
    }

用newCondition新建三个Condition,每个线程持有一个当前线程的Condition和下一个线程的Condition.当前线程打印后,先发送下一个线程的唤醒信号,在调用await进入等待。

Thread[Thread-3,5,main] 0
Thread[Thread-4,5,main] 1
Thread[Thread-5,5,main] 2
Thread[Thread-3,5,main] 3
Thread[Thread-4,5,main] 4
Thread[Thread-5,5,main] 5
...
Thread[Thread-4,5,main] 97
Thread[Thread-5,5,main] 98
Thread[Thread-3,5,main] 99
Thread[Thread-4,5,main] end2
Thread[Thread-5,5,main] end2
Thread[Thread-3,5,main] end2

三个线程循环打印0,1,2,3,…99.