各种JUC同步锁:CountDownLatch、CyclicBarrier、MarriagePhaser、ReadWriteLock、Semaphore、Exchanger、LockSupport

1、CountDownLatch

CountDownLatch是一个同步工具类,用来协调多个线程之间的同步,或者说起到线程之间的通信(而不是用作互斥的作用)。
CountDownLatch能够使一个线程在等待另外一些线程完成各自工作之后,再继续执行。使用一个计数器进行实现。计数器初始值为线程的数量。当每一个线程完成自己任务后,计数器的值就会减一。当计数器的值为0时,表示所有的线程都已经完成一些任务,然后在CountDownLatch上等待的线程就可以恢复执行接下来的任务。
CountDownLatch的用法:
①、某一线程在开始运行前等待n个线程执行完毕。将CountDownLatch的计数器初始化为new CountDownLatch(n),每当一个任务线程执行完毕,就将计数器减1 countdownLatch.countDown(),当计数器的值变为0时,在CountDownLatch上await()的线程就会被唤醒。一个典型应用场景就是启动一个服务时,主线程需要等待多个组件加载完毕,之后再继续执行。
②、实现多个线程开始执行任务的最大并行性。注意是并行性,不是并发,强调的是多个线程在某一时刻同时开始执行。类似于赛跑,将多个线程放到起点,等待发令枪响,然后同时开跑。做法是初始化一个共享的CountDownLatch(1),将其计算器初始化为1,多个线程在开始执行任务前首先countdownlatch.await(),当主线程调用countDown()时,计数器变为0,多个线程同时被唤醒。
CountDownLatch的不足:CountDownLatch是一次性的,计算器的值只能在构造方法中初始化一次,之后没有任何机制再次对其设置值,当CountDownLatch使用完毕后,它不能再次被使用。

2、CyclicBarrier

①、CyclicBarrier和CountDownLatch比较类似。
区别:CountDownLatch计数器只能使用一次,CyclicBarrier计数器可以通过reset方法重置。CyclicBarrier可以查看阻塞线程数量(getNumberWaiting),判断线程是否被阻塞(isBroken)的方法。
②、 构造函数
函数parties参数指的是,等待多少个线程进入屏障点,即等待多少线程调用await方法,才算所有线程都达到屏障点。函数barrierAction指的是当所有线程达到这个屏障以后,将执行此barrierAction的内容。
③、await挂起当前线程,直到所有线程都达到屏障点时再继续执行。支持让线程等待一定的时间,如果到时间以后还有线程没有达到屏障点,那么让已经达到屏障点的线程继续执行。
④、dowait首先检查是否所有线程都达到屏障状态了,如果是,那么执行构造函数第二个参数barrierAction执行的任务。如果不是所有线程都达到屏障状态,那么当前线程挂起。如果线程挂起时指定了挂起时间,那么当时间到以后,此线程被唤醒,接着唤醒此时已经达到屏障状态的线程。

3、MarriagePhaser

CountDownLatch和CyclicBarrier的综合体,是栅栏,但不是循环的,而是分阶段的,每个阶段都有不同的线程可以走,但有的线程到了某个阶段就停止了。每个阶段可以有不同数量的线程等待前进到另一个阶段。线程通过调用 arriAndAwaitAdvance() 来阻止它到达屏障,这是一种阻塞方法。当数量到达等于注册的数量时,程序的执行将继续,并且数量将增加。当线程完成其工作时,我们应该调用arrivalAndDeregister()方法来表示在此特定阶段不再考虑当前线程。

4、ReadWriteLock

①、Java并发库中ReetrantReadWriteLock实现了ReadWriteLock接口并添加了可重入的特性
②、ReetrantReadWriteLock读写锁的效率明显高于synchronized关键字
③、ReetrantReadWriteLock读写锁的实现中,读锁使用共享模式;写锁使用独占模式,换句话说,读锁可以在没有写锁的时候被多个线程同时持有,写锁是独占的
④、ReetrantReadWriteLock读写锁的实现中,需要注意的,当有读锁时,写锁就不能获得;而当有写锁时,除了获得写锁的这个线程可以获得读锁外,其他线程不能获得读锁

5、Semaphore

Semaphore用于限制可以访问某些资源(物理或逻辑的)的线程数目,他维护了一个许可证集合,有多少资源需要限制就维护多少许可证集合,假如这里有N个资源,那就对应于N个许可证,同一时刻也只能有N个线程访问。一个线程获取许可证就调用acquire方法,用完了释放资源就调用release方法。
例如:假如有3个窗口可以打饭,同一时刻也只能有3名同学打饭。第四个人来了之后就必须在外面等着,只要有打饭的同学好了,就可以去相应的窗口了。
对于Semaphore来说,我们需要记住的其实是资源的互斥而不是资源的同步,在同一时刻是无法保证同步的,但是却可以保证资源的互斥。
①、acquire(int permits)
从此信号量获取给定数目的许可,在提供这些许可前一直将线程阻塞,或者线程已被中断。就好比是一个学生占两个窗口。这同时也对应了相应的release方法。
②、release(int permits)
释放给定数目的许可,将其返回到信号量。这个是对应于上面的方法,一个学生占几个窗口完事之后还要释放多少
③、availablePermits()
返回此信号量中当前可用的许可数。也就是返回当前还有多少个窗口可用。
④、reducePermits(int reduction)
根据指定的缩减量减小可用许可的数目。
⑤、hasQueuedThreads()
查询是否有线程正在等待获取资源。
⑥、getQueueLength()
返回正在等待获取的线程的估计数目。该值仅是估计的数字。
⑦、tryAcquire(int permits, long timeout, TimeUnit unit)
如果在给定的等待时间内此信号量有可用的所有许可,并且当前线程未被中断,则从此信号量获取给定数目的许可。
⑧、acquireUninterruptibly(int permits)
从此信号量获取给定数目的许可,在提供这些许可前一直将线程阻塞。基本上常见的使用方法都在这,Semaphore底层是由AQS和Uasafe完成的

6、Exchanger

①、Exchanger,并发工具类,用于线程间的数据交换。
②、两个线程,两个缓冲区,一个线程往一个缓冲区里面填数据,另一个线程从另一个缓冲区里面取数据。当填数据的线程将缓冲区填满时,或者取数据的线程将缓冲区里的数据取空时,就主动向对方发起交换缓冲区的动作,而交换的时机是,一个缓冲区满,另一个缓冲区空。
注意:使用Exchanger来对线程进行数据操作时,线程必须是成对的(线程数量为双数)。
③、exchange(V x):等待另一个线程到达此交换点(除非当前线程被中断),然后将给定的对象传送给该线程,并接收该线程的对象。
④、exchange(V x, long timeout, TimeUnit unit):等待另一个线程到达此交换点(除非当前线程被中断,或者超出了指定的等待时间),然后将给定的对象传送给该线程,同时接收该线程的对象。
使用Exchanger可以对两个线程进行多次的数据交换。
使用Exchanger对两个线程进行数据交换时,线程A发送的东西,与线程B接收到的东西是一样的,即连地址都是一样的。

7、LockSupport

LockSupport主要用于对线程执行暂停(park)和唤醒(unpark)

================================================================================
示例代码:
1、CountDownLatch

public class Thread_001_CountDownLatch {

    public static void main(String[] args) {
        countDownLatch();
    }

    public static void countDownLatch() {
        Thread[] threads = new Thread[5];
        CountDownLatch countDownLatch = new CountDownLatch(3);
        for (int i = 0; i < threads.length; i++) {
            int finalI = i;
            threads[i] = new Thread(() -> {
                int count = 0;
                System.out.println("子线程-" + Thread.currentThread().getName() + "-" + finalI + "-start");
                try {
                    TimeUnit.SECONDS.sleep(threads.length);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("子线程-" + Thread.currentThread().getName() + "-" + finalI + "-end");
                countDownLatch.countDown();
            });
        }

        for (int i = 0; i < threads.length; i++) {
            threads[i].start();
            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        try {
            System.out.println("主线程"+Thread.currentThread().getName()+"等待子线程执行完成...");
            countDownLatch.await();
            System.out.println("子线程"+Thread.currentThread().getName()+"执行完成");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

运行结果:

子线程-Thread-0-0-start
子线程-Thread-1-1-start
子线程-Thread-2-2-start
子线程-Thread-3-3-start
子线程-Thread-4-4-start
子线程-Thread-0-0-end
主线程main等待子线程执行完成...
子线程-Thread-1-1-end
子线程-Thread-2-2-end
子线程main执行完成
子线程-Thread-3-3-end
子线程-Thread-4-4-end

2、CyclicBarrier

public class Thread_002_CyclicBarrier {
    public static void main(String[] args) {
        SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss");
        CyclicBarrier cyclicBarrier = new CyclicBarrier(5,
                ()-> System.out.println("时间-" + sdf.format(new Date()) + "-回调线程-工作线程到达屏障点后开始执行"));
        for (int i = 0; i < 5; i++) {
            String name = "Thread-" + i;
            new Thread(() -> {
                try {
                    TimeUnit.SECONDS.sleep(1);
                    System.out.println("时间-" + sdf.format(new Date()) + "-工作线程-" + name + " 完成任务,进入屏障点,等待其他线程");
                    cyclicBarrier.await();
                    System.out.println("时间-" + sdf.format(new Date()) + "-工作线程-" + name + " 所有线程已进入屏障点,继续执行");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } catch (BrokenBarrierException e) {
                    e.printStackTrace();
                }
            }).start();
        }
    }
}

运行结果:

时间-10:10:20-工作线程-Thread-0 完成任务,进入屏障点,等待其他线程
时间-10:10:20-工作线程-Thread-1 完成任务,进入屏障点,等待其他线程
时间-10:10:20-工作线程-Thread-4 完成任务,进入屏障点,等待其他线程
时间-10:10:20-工作线程-Thread-3 完成任务,进入屏障点,等待其他线程
时间-10:10:20-工作线程-Thread-2 完成任务,进入屏障点,等待其他线程
时间-10:10:20-回调线程-工作线程到达屏障点后开始执行
时间-10:10:20-工作线程-Thread-2 所有线程已进入屏障点,继续执行
时间-10:10:20-工作线程-Thread-1 所有线程已进入屏障点,继续执行
时间-10:10:20-工作线程-Thread-4 所有线程已进入屏障点,继续执行
时间-10:10:20-工作线程-Thread-0 所有线程已进入屏障点,继续执行
时间-10:10:20-工作线程-Thread-3 所有线程已进入屏障点,继续执行

3、MarriagePhaser

public class Thread_003_Phaser {
    static Random random = new Random();
    static MarriagePhaser marriagePhaser = new MarriagePhaser();

    static void sleepMills(int mills) {
        try {
            TimeUnit.MILLISECONDS.sleep(mills);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    static class MarriagePhaser extends Phaser {
        @Override
        protected boolean onAdvance(int phase, int registeredParties) {
            switch (phase) {
                case 0:
                    System.out.println("所有人到齐了!" + registeredParties);
                    System.out.println();
                    return false;
                case 1:
                    System.out.println("所有人吃完了!" + registeredParties);
                    System.out.println();
                    return false;
                case 2:
                    System.out.println("所有人离开了!" + registeredParties);
                    System.out.println();
                    return false;
                case 3:
                    System.out.println("婚礼结束!新郎新娘抱抱!" + registeredParties);
                    return true;
                default:
                    return true;
            }
        }
    }

    static class Person implements Runnable {

        String name;

        public Person(String name) {
            this.name = name;
        }

        public void arrive() {
            sleepMills(random.nextInt(3000));
            System.out.printf("%s 到达现场! \n", name);
            marriagePhaser.arriveAndAwaitAdvance();
        }

        public void eat() {
            sleepMills(random.nextInt(3000));
            System.out.printf("%s 吃完! \n", name);
            marriagePhaser.arriveAndAwaitAdvance();
        }

        public void leave() {
            sleepMills(random.nextInt(3000));
            System.out.printf("%s 离开! \n", name);
            marriagePhaser.arriveAndAwaitAdvance();
        }

        private void hug() {
            if("新郎".equals(name) || "新娘".equals(name)) {
                sleepMills(random.nextInt(3000));
                System.out.printf("%s 洞房! \n", name);
                marriagePhaser.arriveAndAwaitAdvance();
            } else {
                marriagePhaser.arriveAndAwaitAdvance();
            }
        }

        @Override
        public void run() {
            arrive();
            eat();
            leave();
            hug();
        }
    }

    public static void main(String[] args) {
        marriagePhaser.bulkRegister(7);
        for (int i = 0; i < 5; i++) {
            new Thread(new Person("宾客" + i)).start();
        }
        new Thread(new Person("新郎")).start();
        new Thread(new Person("新娘")).start();
    }
}

运行结果:

宾客3 到达现场! 
宾客2 到达现场! 
宾客1 到达现场! 
宾客4 到达现场! 
宾客0 到达现场! 
新娘 到达现场! 
新郎 到达现场! 
所有人到齐了!7

宾客1 吃完! 
宾客2 吃完! 
新娘 吃完! 
宾客3 吃完! 
宾客0 吃完! 
宾客4 吃完! 
新郎 吃完! 
所有人吃完了!7

宾客3 离开! 
宾客1 离开! 
宾客0 离开! 
新娘 离开! 
宾客4 离开! 
新郎 离开! 
宾客2 离开! 
所有人离开了!7

新郎 洞房! 
新娘 洞房! 
婚礼结束!新郎新娘抱抱!7
/**
 * Description: TODO
 * 题目:5个学生一起参加考试,一共有三道题,要求所有学生到齐才能开始考试,
 * 全部同学都做完第一题,学生才能继续做第二题,全部学生做完了第二题,才能做第三题,
 * 所有学生都做完的第三题,考试才结束。
 * 分析这个题目:这是一个多线程(5个学生)分阶段问题(考试开始、第一题做完、第二题做完、第三题做完)
 * @title: Thread_004_Phaser
 * Created by 19040838 on 2020.12.08 11:30
 */
public class Thread_004_Phaser {

    static SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss SSS");

    static Random random = new Random();

    static void sleepmills(int mills) {
        try {
            TimeUnit.SECONDS.sleep(mills);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    static class MarriagePhaser extends Phaser {
        @Override
        protected boolean onAdvance(int phase, int registeredParties) {
            switch (phase) {
                case 0:
                    System.out.println("学生准备好了,学生人数:" + getRegisteredParties());
                    return false;
                case 1:
                    System.out.println("第一题所有学生做完");
                    return false;
                case 2:
                    System.out.println("第二题所有学生做完");
                    return false;
                case 3:
                    System.out.println("第三题所有学生做完,结束考试");
                    return false;
                default:
                    return true;
            }
        }
    }

    static class StudentTask implements Runnable {

        private Phaser phaser;

        public StudentTask(Phaser phaser) {
            this.phaser = phaser;
        }

        @Override
        public void run() {
            System.out.println(Thread.currentThread().getName() + "---到达考场时间---" + sdf.format(new Date()));
            phaser.arriveAndAwaitAdvance();

            System.out.println(Thread.currentThread().getName() + "---做第1题开始时间---" + sdf.format(new Date()));
            sleepmills(random.nextInt(10));
            System.out.println(Thread.currentThread().getName() + "---做第1题完成---" + sdf.format(new Date()));
            phaser.arriveAndAwaitAdvance();

            System.out.println(Thread.currentThread().getName() + "---做第2题时间---" + sdf.format(new Date()));
            sleepmills(random.nextInt(10));
            System.out.println(Thread.currentThread().getName() + "---做第2题完成---" + sdf.format(new Date()));
            phaser.arriveAndAwaitAdvance();

            System.out.println(Thread.currentThread().getName() + "---做第3题时间---" + sdf.format(new Date()));
            sleepmills(random.nextInt(10));
            System.out.println(Thread.currentThread().getName() + "---做第3题完成---" + sdf.format(new Date()));
            phaser.arriveAndAwaitAdvance();
        }
    }

    public static void main(String[] args) {
        MarriagePhaser phaser = new MarriagePhaser();
        StudentTask[] studentTask = new StudentTask[5];
        for (int i = 0; i < studentTask.length; i++) {
            studentTask[i] = new StudentTask(phaser);
            phaser.register();	//注册一次表示phaser维护的线程个数
        }

        Thread[] threads = new Thread[studentTask.length];
        for (int i = 0; i < studentTask.length; i++) {
            threads[i] = new Thread(studentTask[i], "Student---" + i);
            threads[i].start();
        }

        //等待所有线程执行结束
        for (int i = 0; i < studentTask.length; i++) {
            try {
                threads[i].join();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        System.out.println("Phaser has finished:" + phaser.isTerminated());
    }
}

运行结果:

Student---1---到达考场时间---14:02:47 284
Student---3---到达考场时间---14:02:47 284
Student---0---到达考场时间---14:02:47 284
Student---2---到达考场时间---14:02:47 284
Student---4---到达考场时间---14:02:47 284
学生准备好了,学生人数:5
Student---2---做第1题开始时间---14:02:47 287
Student---4---做第1题开始时间---14:02:47 287
Student---1---做第1题开始时间---14:02:47 288
Student---3---做第1题开始时间---14:02:47 288
Student---0---做第1题开始时间---14:02:47 288
Student---2---做第1题完成---14:02:47 727
Student---4---做第1题完成---14:02:48 467
Student---3---做第1题完成---14:02:49 008
Student---1---做第1题完成---14:02:49 512
Student---0---做第1题完成---14:02:49 528
第一题所有学生做完
Student---3---做第2题时间---14:02:49 528
Student---4---做第2题时间---14:02:49 528
Student---1---做第2题时间---14:02:49 528
Student---2---做第2题时间---14:02:49 528
Student---0---做第2题时间---14:02:49 528
Student---3---做第2题完成---14:02:49 594
Student---2---做第2题完成---14:02:49 727
Student---4---做第2题完成---14:02:49 995
Student---1---做第2题完成---14:02:51 399
Student---0---做第2题完成---14:02:52 437
第二题所有学生做完
Student---2---做第3题时间---14:02:52 437
Student---0---做第3题时间---14:02:52 437
Student---3---做第3题时间---14:02:52 437
Student---1---做第3题时间---14:02:52 437
Student---4---做第3题时间---14:02:52 437
Student---4---做第3题完成---14:02:53 267
Student---0---做第3题完成---14:02:53 755
Student---3---做第3题完成---14:02:53 985
Student---1---做第3题完成---14:02:55 204
Student---2---做第3题完成---14:02:55 294
第三题所有学生做完,结束考试
Phaser对象是否销毁:false

4、ReadWriteLock

public class Thread_005_ReadWriteLock {
    static ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
    static Lock readLock = readWriteLock.readLock();
    static Lock writeLock = readWriteLock.writeLock();
    public static void read(Lock lock, int index) {
        try {
            lock.lock();
            System.out.println("读Thread-" + index + "开始");
            TimeUnit.SECONDS.sleep(5);
            System.out.println("读Thread-" + index + "结束");
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }

    public static void write(Lock lock, int index) {
        try {
            lock.lock();
            System.out.println("写Thread-" + index + "开始");
            TimeUnit.SECONDS.sleep(5);
            System.out.println("写Thread-" + index + "结束");
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }

    public static void main(String[] args) {
        for (int i = 0; i < 18; i++) {
            int finalI = i;
            new Thread(()->read(readLock, finalI)).start();
        }

        for (int i = 0; i < 2; i++) {
            int finalI = i;
            new Thread(()->write(writeLock, finalI)).start();
        }
    }
}

运行结果:

读Thread-0开始
读Thread-1开始
读Thread-2开始
读Thread-3开始
读Thread-4开始
读Thread-6开始
读Thread-5开始
读Thread-7开始
读Thread-8开始
读Thread-9开始
读Thread-10开始
读Thread-11开始
读Thread-12开始
读Thread-13开始
读Thread-14开始
读Thread-15开始
读Thread-17开始
读Thread-16开始
读Thread-0结束
读Thread-2结束
读Thread-1结束
读Thread-3结束
读Thread-4结束
读Thread-8结束
读Thread-7结束
读Thread-9结束
读Thread-10结束
读Thread-5结束
读Thread-6结束
读Thread-14结束
读Thread-11结束
读Thread-16结束
读Thread-13结束
读Thread-17结束
读Thread-15结束
读Thread-12结束
写Thread-0开始
写Thread-0结束
写Thread-1开始
写Thread-1结束

5、Semaphore

public class Thread_006_Semaphore {
    public static void main(String[] args) {
        Semaphore semaphore = new Semaphore(2, true);
        new Thread(()->{
            try {
                semaphore.acquire();
                System.out.println("thread1----start");
                Thread.sleep(200);
                System.out.println("thread1----end");
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                semaphore.release();
            }
        }).start();
        new Thread(()->{
            try {
                semaphore.acquire();
                System.out.println("thread2----start");
                Thread.sleep(200);
                System.out.println("thread2----end");
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                semaphore.release();
            }
        }).start();
    }
}

运行结果:

thread1----start
thread2----start
thread1----end
thread2----end

6、Exchanger

public class Thread_007_Exchanger {
    static Exchanger<String> exchanger = new Exchanger<>();

    public static void main(String[] args) {
        new Thread(()->{
           String str = "exchanger1";
            try {
                str = exchanger.exchange(str);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + " " + str);
        }, "Thread1").start();
        new Thread(()->{
            String str = "exchanger2";
            try {
                str = exchanger.exchange(str);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + " " + str);
        }, "Thread2").start();
    }

运行结果:

Thread2 exchanger1
Thread1 exchanger2

7、LockSupport

public class Thread_008_LockSupport {
    public static void main(String[] args) {
        Thread t = new Thread(()->{
            for (int i = 0; i < 10; i++) {
                System.out.println(i);
                try {
                    if(i == 5) {
                        LockSupport.park();
                        System.out.println("tingyixia");
                        TimeUnit.SECONDS.sleep(5);
                    } else {
                        TimeUnit.SECONDS.sleep(1);
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });

        t.start();

        LockSupport.unpark(t);

        /*try {
            TimeUnit.SECONDS.sleep(8);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("after 8 senconds!");

        LockSupport.unpark(t);*/
    }
}

运行结果:

0
1
2
3
4
5
tingyixia
6
7
8
9
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
CountDownLatch 是一个计数器,它可以让一个或多个线程等待其他线程执行完毕后再继续执行。它的主要方法是 countDown() 和 await(),其中 countDown() 用于计数减一,await() 用于等待计数器变为0。与 CountDownLatch 相比,CyclicBarrier 的主要区别在于它可以重复使用,而且所有线程必须同时到达栅栏处才能继续执行后续任务。CyclicBarrier 的重要方法是 await(),并且可以通过构造方法传入一个 Runnable,在所有线程都到达栅栏状态时优先执行该动作。CyclicBarrier 内部使用 ReentrantLock 和 Condition 实现等待和唤醒的功能,通过维护一个 count 变量来记录还有多少个线程没有到达栅栏处。 <span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [CountDownLatchCyclicBarrier](https://blog.csdn.net/weixin_44442186/article/details/123985119)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *2* *3* [JUC多线程CountDownLatchCyclicBarrierSemaphore同步器原理总结](https://blog.csdn.net/a745233700/article/details/120688546)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

软软的铲屎官

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值