日常学习记录Java 常用线程并发类的应用实例
Posted 盛夏温暖流年
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了日常学习记录Java 常用线程并发类的应用实例相关的知识,希望对你有一定的参考价值。
之前复习线程并发的时候曾经写过博客,对 ReentrantLock 的实现原理进行了详细的介绍,链接如下:
https://blog.csdn.net/j1231230/article/details/120572008
常用常新,趁着还没忘再回顾一下相关知识,保持学习的好习惯非常重要。
可以知道,ReentrantLock 实现的前提是 AbstractQueuedSynchronizer(抽象队列同步器),简称 AQS,是 java.util.concurrent 的核心,常用的线程并发类 CountDownLatch、CyclicBarrier、Semaphore 等都包括了一个继承自 AQS 抽象类的内部类。
本篇博客对这几个线程并发类 CountDownLatch、CyclicBarrier、Semaphore 的应用实例进行记录和介绍。
CountDownLatch
CountDownLatch(倒计时器):用来协调多个线程之间的同步。
比如在主线程中新建 n 个子线程,那 CountDownLatch 的参数需要设置的和线程数一致,当子线程都执行完成后,倒计时归零,此时等待的主线程才能继续执行。
代码应用实例
public class CountDownLatchDemo {
public static void main(String[] args) throws InterruptedException {
CountDownLatch countDownLatch = new CountDownLatch(2);
new Thread() {
@Override
public void run() {
try {
System.out.println("执行任务1");
Thread.sleep(1000);
System.out.println("执行任务1完毕");
countDownLatch.countDown();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}.start();
new Thread() {
@Override
public void run() {
try {
System.out.println("执行任务2");
Thread.sleep(1000);
System.out.println("执行任务2完毕");
countDownLatch.countDown();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}.start();
countDownLatch.await();
// 两个都执行完毕才执行
System.out.println("exit");
}
}
运行结果如下所示:
我们在主线程中新建了 2 个子线程,这里的 CountDownLatch 的参数设置和子线程数保持一致,都是 2,当子线程都执行完成后倒计时归零,此时等待的主线程就能继续执行了,输出结果 “exit” 。
CyclicBarrier
CyclicBarrier(循环栅栏):它的功能比 CountDownLatch 更加复杂和强大,主要作用是让一组线程到达一个同步点时被阻塞,直到最后一个线程到达同步点时,屏障才会被解除。
举一个具体的例子,比如一个公司聚餐,每个员工都从家里出发,可以把每个人都看作是一个线程,每个线程调⽤ await() ⽅法告诉 CyclicBarrier 我已经到达了屏障,然后当前线程会被阻塞。等到最后一个员工到了(屏障解除),饭店才打开门让大家进去(线程才会开始工作)。
代码应用实例
public class CyclicBarrierDemo {
public static void main(String[] args) {
CyclicBarrier cyclicBarrier = new CyclicBarrier(3, new Runnable() {
@Override
public void run() {
System.out.println("全员到齐,开始吃饭!");
}
});
ExecutorService es = Executors.newCachedThreadPool();
try {
for (int i = 0; i < 3; i++) {
int user = i + 1;
Runnable r = new Runnable() {
@Override
public void run() {
try {
Thread.sleep(1000);
System.out.println("用户" + user + "已到达");
cyclicBarrier.await();
System.out.println("我去吃饭了!");
} catch (Exception e) {
e.printStackTrace();
}
}
};
es.submit(r);
}
}catch (Exception e){
e.printStackTrace();
}finally {
es.shutdown();
}
}
}
运行结果如下所示:
分析可以得知,用户2的线程先运行,在原地阻塞等待;之后用户3的线程运行,也进入等待状态,最后用户1的线程运行,此时屏障得以解除,3 个线程才会正式开始工作。
Semaphore
Semaphore (信号量):用来控制同时访问特定资源的线程数量,通过协调各个线程,以保证资源的合理利用。
代码应用实例
public class SemaphoreDemo {
public static void main(String[] args) {
int workers = 8;
Semaphore semaphore = new Semaphore(5);
for (int i=0; i<workers;i++){
new Worker(i, semaphore).start();
}
}
}
class Worker extends Thread{
private Integer num;
private Semaphore semaphore;
public Worker(Integer num, Semaphore semaphore){
this.num = num;
this.semaphore = semaphore;
}
@Override
public void run(){
try {
semaphore.acquire();
System.out.println("工人" + this.num + ",占用了一台机器");
Thread.sleep(2000);
System.out.println("工人" + this.num + ",释放了一台机器");
semaphore.release();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
运行结果如下所示:
以上实例中,共有 8 个工人访问 5 台工作机器,也就是说 8 个线程中可以有 5 个同时进行操作,此时剩下的 3 个线程需要等待,等有线程完成操作,资源空闲下来后,其他线程才可以继续使用。
以上是关于日常学习记录Java 常用线程并发类的应用实例的主要内容,如果未能解决你的问题,请参考以下文章