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

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


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]获取到锁后休眠21603284047347 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.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值