使用ReentrantLock的Condition交替输出数组中元素的方法 && 指定执行顺序

package com.example.demo.testLock;

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;

/***/
public class TestReentrantLock {

    public static void main(String[] args) {

        String[] number = {"1", "2", "3", "4", "5"};
        String[] str = {"A", "B", "C", "D", "E"};

        // new一把锁
        ReentrantLock reentrantLock = new ReentrantLock();
        // 第一个队列
        Condition condition1 = reentrantLock.newCondition();
        // 第二个队列
        Condition condition2 = reentrantLock.newCondition();
        new Thread(()->{
        	// 线程执行的时候加个锁,保证只有当前线程执行
            reentrantLock.lock();
            for (String num : number){
                try {
                    System.out.print(num);
                    // signal,唤醒阻塞的线程
                    condition2.signal();
                    // await,当前线程阻塞挂起
                    condition1.await();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
			// 这个必须加,不加的话,那么下面那个线程跑完了,会一直await了,然后无法解锁了,必须互相通知相互唤醒
            condition2.signal();
            reentrantLock.unlock();
        }).start();

        new Thread(()->{
            reentrantLock.lock();
            for (String s : str){
                try {
                    System.out.print(s);
                    condition1.signal();
                    condition2.await();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }

            condition1.signal();
            reentrantLock.unlock();
        }).start();

    }
}

上面的代码,网上大部分都能找到,先不关注源码如何实现的(我看不懂),想一个问题,我如果在两个线程外面,包一个for循环,循环10次,是什么打印结果呢? 另外我如何保证两个线程的顺序执行呢?现在只是保证了交替执行,怎么保证先输出A再输出1呢?

第一个问题,开了for循环,假如我们for 10次,会根据线程执行的顺序,交替打印完1A,1A,1A,1A 10次,然后打印2B,2B,2B 10次,当然中间很可能答应出了B2,B2这种顺序

第二个问题,如何保证顺序执行呢?例如我现在一定要保证输出A1,B2,C3,D4,E5这种顺序

答案:加个门栓,CountDownLatch

        CountDownLatch countDownLatch = new CountDownLatch(1);

        new Thread(()->{
            try {
                countDownLatch.await();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            reentrantLock.lock();

            for (String num : number){
                try {
                    log.info(num);
                    condition2.signal();
                    condition1.await();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }

            condition2.signal();
            reentrantLock.unlock();
        }).start();

        new Thread(()->{
            reentrantLock.lock();
            for (String s : str){
                try {
                    log.info(s);
                    countDownLatch.countDown();
                    condition1.signal();
                    condition2.await();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }

            condition1.signal();
            reentrantLock.unlock();
        }).start();

我们现在加了门栓保证了顺序执行,那么外面再加个for循环,会发生什么呢?发现乱序了,没有一个一个顺序输出了,代码如下

@Slf4j
public class TestReentrantLock {

    public static void main(String[] args) {

        String[] number = {"1", "2", "3", "4", "5"};
        String[] str = {"A", "B", "C", "D", "E"};

        // new一把锁
        ReentrantLock reentrantLock = new ReentrantLock();
        // 第一个队列
        Condition condition1 = reentrantLock.newCondition();
        // 第二个队列
        Condition condition2 = reentrantLock.newCondition();

        CountDownLatch countDownLatch = new CountDownLatch(1);

        for(int i = 0;i<5;i++){
        new Thread(()->{
            try {
                countDownLatch.await();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            reentrantLock.lock();

            for (String num : number){
                try {
                    log.info("countDownLatch:{},num:{}", countDownLatch.getCount(), num);
                    condition2.signal();
                    condition1.await();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }

            condition2.signal();
            reentrantLock.unlock();
        }).start();

        new Thread(()->{
            reentrantLock.lock();
            for (String s : str){
                try {
                    countDownLatch.countDown();
                    log.info("countDownLatch:{},string:{}", countDownLatch.getCount(), s);
                    condition1.signal();
                    condition2.await();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }

            condition1.signal();
            reentrantLock.unlock();
        }).start();
        }
    }
}

/**
 * todo 输出结果如下 可以看到线程1,0,2,3交替输出,
 * 一开始,线程1先执行,然后线程0执行,符合了预期,但是后面变为了线程2限制性,
 * 先打印了【1】,然后打印【A】,我们想要的是什么,是先打印【A】,然后打印【1】
 */
16:04:47.264 [Thread-1] INFO com.example.demo.testLock.TestReentrantLock - countDownLatch:0,string:A
16:04:47.275 [Thread-0] INFO com.example.demo.testLock.TestReentrantLock - countDownLatch:0,num:1
16:04:47.275 [Thread-2] INFO com.example.demo.testLock.TestReentrantLock - countDownLatch:0,num:1
16:04:47.275 [Thread-3] INFO com.example.demo.testLock.TestReentrantLock - countDownLatch:0,string:A
16:04:47.275 [Thread-4] INFO com.example.demo.testLock.TestReentrantLock - countDownLatch:0,num:1
16:04:47.275 [Thread-5] INFO com.example.demo.testLock.TestReentrantLock - countDownLatch:0,string:A
16:04:47.275 [Thread-6] INFO com.example.demo.testLock.TestReentrantLock - countDownLatch:0,num:1
16:04:47.276 [Thread-7] INFO com.example.demo.testLock.TestReentrantLock - countDownLatch:0,string:A
16:04:47.276 [Thread-8] INFO com.example.demo.testLock.TestReentrantLock - countDownLatch:0,num:1
16:04:47.276 [Thread-9] INFO com.example.demo.testLock.TestReentrantLock - countDownLatch:0,string:A
16:04:47.276 [Thread-1] INFO com.example.demo.testLock.TestReentrantLock - countDownLatch:0,string:B
16:04:47.276 [Thread-0] INFO com.example.demo.testLock.TestReentrantLock - countDownLatch:0,num:2
16:04:47.276 [Thread-3] INFO com.example.demo.testLock.TestReentrantLock - countDownLatch:0,string:B
16:04:47.276 [Thread-2] INFO com.example.demo.testLock.TestReentrantLock - countDownLatch:0,num:2
16:04:47.276 [Thread-5] INFO com.example.demo.testLock.TestReentrantLock - countDownLatch:0,string:B
16:04:47.276 [Thread-4] INFO com.example.demo.testLock.TestReentrantLock - countDownLatch:0,num:2
16:04:47.276 [Thread-7] INFO com.example.demo.testLock.TestReentrantLock - countDownLatch:0,string:B
16:04:47.276 [Thread-6] INFO com.example.demo.testLock.TestReentrantLock - countDownLatch:0,num:2
16:04:47.277 [Thread-8] INFO com.example.demo.testLock.TestReentrantLock - countDownLatch:0,num:2
16:04:47.277 [Thread-9] INFO com.example.demo.testLock.TestReentrantLock - countDownLatch:0,string:B
16:04:47.277 [Thread-0] INFO com.example.demo.testLock.TestReentrantLock - countDownLatch:0,num:3
16:04:47.277 [Thread-1] INFO com.example.demo.testLock.TestReentrantLock - countDownLatch:0,string:C
16:04:47.277 [Thread-2] INFO com.example.demo.testLock.TestReentrantLock - countDownLatch:0,num:3
16:04:47.278 [Thread-3] INFO com.example.demo.testLock.TestReentrantLock - countDownLatch:0,string:C
16:04:47.278 [Thread-4] INFO com.example.demo.testLock.TestReentrantLock - countDownLatch:0,num:3
16:04:47.278 [Thread-5] INFO com.example.demo.testLock.TestReentrantLock - countDownLatch:0,string:C
16:04:47.278 [Thread-7] INFO com.example.demo.testLock.TestReentrantLock - countDownLatch:0,string:C
16:04:47.278 [Thread-6] INFO com.example.demo.testLock.TestReentrantLock - countDownLatch:0,num:3
16:04:47.278 [Thread-9] INFO com.example.demo.testLock.TestReentrantLock - countDownLatch:0,string:C
16:04:47.278 [Thread-8] INFO com.example.demo.testLock.TestReentrantLock - countDownLatch:0,num:3
16:04:47.278 [Thread-1] INFO com.example.demo.testLock.TestReentrantLock - countDownLatch:0,string:D
16:04:47.278 [Thread-0] INFO com.example.demo.testLock.TestReentrantLock - countDownLatch:0,num:4
16:04:47.278 [Thread-3] INFO com.example.demo.testLock.TestReentrantLock - countDownLatch:0,string:D
16:04:47.278 [Thread-2] INFO com.example.demo.testLock.TestReentrantLock - countDownLatch:0,num:4
16:04:47.279 [Thread-4] INFO com.example.demo.testLock.TestReentrantLock - countDownLatch:0,num:4
16:04:47.279 [Thread-5] INFO com.example.demo.testLock.TestReentrantLock - countDownLatch:0,string:D
16:04:47.279 [Thread-6] INFO com.example.demo.testLock.TestReentrantLock - countDownLatch:0,num:4
16:04:47.279 [Thread-7] INFO com.example.demo.testLock.TestReentrantLock - countDownLatch:0,string:D
16:04:47.279 [Thread-8] INFO com.example.demo.testLock.TestReentrantLock - countDownLatch:0,num:4
16:04:47.279 [Thread-9] INFO com.example.demo.testLock.TestReentrantLock - countDownLatch:0,string:D
16:04:47.279 [Thread-0] INFO com.example.demo.testLock.TestReentrantLock - countDownLatch:0,num:5
16:04:47.279 [Thread-1] INFO com.example.demo.testLock.TestReentrantLock - countDownLatch:0,string:E
16:04:47.279 [Thread-3] INFO com.example.demo.testLock.TestReentrantLock - countDownLatch:0,string:E
16:04:47.279 [Thread-2] INFO com.example.demo.testLock.TestReentrantLock - countDownLatch:0,num:5
16:04:47.279 [Thread-5] INFO com.example.demo.testLock.TestReentrantLock - countDownLatch:0,string:E
16:04:47.279 [Thread-4] INFO com.example.demo.testLock.TestReentrantLock - countDownLatch:0,num:5
16:04:47.279 [Thread-7] INFO com.example.demo.testLock.TestReentrantLock - countDownLatch:0,string:E
16:04:47.280 [Thread-6] INFO com.example.demo.testLock.TestReentrantLock - countDownLatch:0,num:5
16:04:47.280 [Thread-9] INFO com.example.demo.testLock.TestReentrantLock - countDownLatch:0,string:E
16:04:47.280 [Thread-8] INFO com.example.demo.testLock.TestReentrantLock - countDownLatch:0,num:5

为什么会出现上面的这种情况,因为你门栓,只是栓了一个线程,当第一个线程执行的时候,门栓变为了0,失去了作用,后面的线程肯定会乱序执行,不会按照你指定的顺序执行了,那么门栓我们调大点,跟循环的次数一致可以么?

例如:CountDownLatch countDownLatch = new CountDownLatch(1);

改为:CountDownLatch countDownLatch = new CountDownLatch(5);

答案是不可以的,因为你放大点,那还是拴住打印字符串的线程,比如我设置5,那底下的线程会打印5次A,然后才执行上面的线程,打印1


我们想要的结果是什么,是这5次循环里面,每次都先打印字符串,然后再打印数字,那么是不是这5次循环,每次的门栓都得是一把新的门栓,然后栓你想要的线程,我们把CountDownLatch countDownLatch = new CountDownLatch(1);放到for循环里面是不是就可以了呢?

答案:依旧是不可以的
两个线程为一组执行的时候,使用的应该是同一个CountDownLatch,对其他线程是不可见的,for循环里,每次实例化一个CountDownLatch,但是最终执行的时候会发现,始终使用的是最后一个CountDownLatch的引用,这是一个问题,为什么?解决了这个问题,就可以保障for循环里面指定输出规则,按照顺序输出了


该如何实现呢?
等这两个线程执行完,然后再次开启下次循环,这样可以,但是效率无法保证了,如何保证一个循环中的两个线程,使用的CountDownLatch是唯一的,每次新建的呢?

使用AtomicReference

// for循环里面,每次都
AtomicReference<CountDownLatch> atomicReference = new AtomicReference(new CountDownLatch(1));
然后底下用 atomicReference.get().await();和 atomicReference.get().countDown();
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值