JUC并发编程之JUC内置常用工具类

CountDownLatch

在日常开发中经常会遇到需要在主线程中开启多个线程去并行执行任务,并且主线程需要等待所有子线程执行完毕后再进行汇总的场景。在CountDownLatch出现之前一般都使用线程的join()方法来实现这一点,但是join方法不够灵活,不能够满足不同场景的需要,所以JDK开发组提供了CountDownLatch这个类能够使一个线程等待其他线程完成各自的工作后再执行。

工作原理

CountDownLatch是通过一个计数器来实现的,计数器的初始值为初始任务的数量。每当完成了一个任务后,计数器的值就会减1(CountDownLatch.countDown()方法)。当计数器值到达0时,它表示所有的已经完成了任务,然后在闭锁上等待CountDownLatch.await()方法的线程就可以恢复执行任务。

CountDownLatch和join的区别

调用一个子线程的join()方法后,该线程会一直阻塞直到子线程运行完毕,而CountDownLatch则使用计数器来允许子线程运行完毕或者在运行中递减计数,也就是CountDownLatch可以在子线程运行的任何时候让await方法返回而不一定必须等到线程结束。另外,使用线程池来管理线程时,一般都是直接添加Runnable到线程池,这时候就没有办法在调用线程的join方法了,也就是说CountDownLatch相比join方法让我们对线程同步有更加灵活的控制。

示例

import java.util.concurrent.CountDownLatch;

public class CountDownLatchDome {
    private int count = 10;

    public int getCount() {
        return this.count;
    }

    public static void main(String[] args) throws Exception{
        CountDownLatch countDownLatch = new CountDownLatch(new CountDownLatchDome().getCount());

        for (int i = 0; i < 10; i++) {

            new Thread(() -> {
                countDownLatch.countDown();  //计数器自减
                System.out.println(Thread.currentThread().getName() + "线程运行结束");
            }, Integer.toString(i)).start();
        }

        countDownLatch.await(); //阻塞知道计数器为0
        System.out.println("所有线程运行结束");
    }
}

运行结果

1线程运行结束
4线程运行结束
6线程运行结束
5线程运行结束
3线程运行结束
2线程运行结束
0线程运行结束
8线程运行结束
7线程运行结束
9线程运行结束
所有线程运行结束

CyclicBarrier

工作原理

CyclicBarrier的字面意思是可循环使用(Cyclic)的屏障(Barrier)。它要做的事情是,让一组线程到达一个屏障(也可以叫同步点)时被阻塞,直到最后一个线程到达屏障时,屏障才会开门,所有被屏障拦截的线程才会继续运行。CyclicBarrier默认的构造方法是CyclicBarrier(int parties),其参数表示屏障拦截的线程数量,每个线程调用await方法告诉CyclicBarrier我已经到达了屏障,然后当前线程被阻塞。

JUC并发编程之JUC内置常用工具类_第1张图片
当abc三个线程都调用await()后,证明三个线程都达到了同步位置,三个线程才继续向下执行,还有未到达await()则需要等待

应用场景

CyclicBarrier可以用于多线程计算数据,最后合并计算结果的场景。

示例

import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;

public class CyclicBarrierDemo {
    public static void main(String[] args) {

         //所有线程执行完毕后执行此线程(箭头函数构造)
        CyclicBarrier cyclicBarrier = new CyclicBarrier(10, () -> {
            System.out.println("所有线程执行完毕!");
        });
        for (int i = 0; i < 10; i++) {
            int temp = i;
            new Thread(() -> {
                System.out.println(Thread.currentThread().getName() + "\t执行"+temp+"线程");

                try {
                    cyclicBarrier.await();   //达到同步点
                } catch (InterruptedException | BrokenBarrierException e) {
                    e.printStackTrace();
                }
            }, String.valueOf(i)).start();
        }
    }
}

运行结果

0	执行0线程
2	执行2线程
1	执行1线程
4	执行4线程
3	执行3线程
5	执行5线程
6	执行6线程
7	执行7线程
8	执行8线程
9	执行9线程
所有线程执行完毕!

CountDownLatch和CyclicBarrier的区别

1.CountDownLatch的计数器只能使用一次,而CyclicBarrier的计数器可以反复使用。
2.CountDownLatch.await一般阻塞工作线程,所有的进行预备工作的线程执行countDown,而CyclicBarrier通过工作线程调用await从而自行阻塞,直到所有工作线程达到指定屏障,再大家一起往下走。
3.在控制多个线程同时运行上,CountDownLatch可以不限线程数量,而CyclicBarrier是固定线程数。
同时,CyclicBarrier还可以提供一个barrierAction,合并多线程计算结果。

Semaphore

Semaphore(信号量)是用来控制同时访问特定资源的线程数量,它通过协调各个线程,以保证合理的使用公共资源。

工作原理

我们可以把它比作是控制流量的红绿灯,比如XX马路要限制流量,只允许同时有一百辆车在这条路上行使,其他的都必须在路口等待,所以前一百辆车会看到绿灯,可以开进这条马路,后面的车会看到红灯,不能驶入XX马路,但是如果前一百辆中有五辆车已经离开了XX马路,那么后面就允许有5辆车驶入马路,这个例子里说的车就是线程,驶入马路就表示线程在执行,离开马路就表示线程执行完成,看见红灯就表示线程被阻塞,不能执行。
(保证同时只有100个线程执行)

应用场景

Semaphore可以用于做流量控制,特别公用资源有限的应用场景,比如数据库连接。假如有一个需求,要读取几万个文件的数据,因为都是IO密集型任务,我们可以启动几十个线程并发的读取,但是如果读到内存后,还需要存储到数据库中,而数据库的连接数只有10个,这时我们必须控制只有十个线程同时获取数据库连接保存数据,否则会报错无法获取数据库连接。这个时候,我们就可以使用Semaphore来做流控。

示例



import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;

/**
 * @author zhaolimin * @date 2021/11/15 * @apiNote 信号量测试
 */
public class SemaphoreDemo {
    public static void main(String[] args) {        
        // 不设置第二个参数默认非公平锁        
        // 模拟5个车位。
        Semaphore semaphore = new Semaphore(5, false);
        // 模拟10辆车来
        for (int i = 1; i <= 10; i++) {
            new Thread(() -> {
                try {
                    semaphore.acquire();  //获取到信号量
                    System.out.println(Thread.currentThread().getName() + "车\t抢到车位。。。");
                    // 暂停一会线程,假装每个车在车位上停三秒
                    TimeUnit.SECONDS.sleep(3);
                    System.out.println(Thread.currentThread().getName() + "车\t停车三秒后离开车位!");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    semaphore.release();  //释放信号量
                }
            }, String.valueOf(i)).start();
        }
    }
}

运行结果

1车	抢到车位。。。
5车	抢到车位。。。
3车	抢到车位。。。
2车	抢到车位。。。
4车	抢到车位。。。
4车	停车三秒后离开车位!
3车	停车三秒后离开车位!
1车	停车三秒后离开车位!
2车	停车三秒后离开车位!
6车	抢到车位。。。
10车	抢到车位。。。
9车	抢到车位。。。
7车	抢到车位。。。
5车	停车三秒后离开车位!
8车	抢到车位。。。
6车	停车三秒后离开车位!
7车	停车三秒后离开车位!
9车	停车三秒后离开车位!
10车	停车三秒后离开车位!
8车	停车三秒后离开车位!

你可能感兴趣的:(JUC并发编程,java,并发编程,juc,多线程,开发工具)