返回

Java多线程(7):JUC(上)

发布时间:2023-02-02 11:25:39 257
# java# java# 技术# 设备# 工具

您好,我是湘王,这是我的51CTO博客,欢迎您来,欢迎您再来~


前面把线程相关的生命周期、关键字、线程池(ThreadPool)、ThreadLocal、CAS、锁和AQS都讲完了,现在就剩下怎么来用多线程了。而要想用好多线程,其实是可以取一些巧的,比如JUC(好多面试官喜欢问的JUC,就是现在要讲的JUC)。JUC就是java.util.concurrent的首字母缩写,是Java并发工具包中提供的各种工具类的统称,主要分为几大类:

1、同步器;

2、线程安全的容器;

3、阻塞队列;

4、一些特殊的类。

他们都有各自适合应用场景。这里是并发工具包相关类的继承结构:

Java多线程(7):JUC(上)_JUC


下面从同步器开始。

常用的JUC同步器有四个:

1、CountDownLatch:字面意思是倒计时,如果开发的系统功能中有“倒计时”的需求,那么CountDownLatch是最合适的工具。它还有一个大家耳熟能详的别称:发令枪。可以想象一下,当裁判员喊:预备~开始的时候,或者火箭点火发射的时候,所有的人、设备、部门都会依次检查确认自己是否准备好行动,只有当全部都确认准备好了才能开始下一个动作,也就是等倒数到指定的数字(一般是0)的时候,就开始执行预设动作;

2、Semaphore:字面意思是「信号量」,好比红绿灯,或者就餐排队时餐馆发的数字序号,一次只允许若干个人过马路或者就餐。这个在昨天的例子里面也已经演示过了,而且还是通过自定义AQS来实现的(信号量可能不太好理解,我更倾向于叫它摇号器/叫号器,比如餐馆门口排队,每次可以进去多少人);

3、CyclicBarrier:字面意思是「屏障」或者「栅栏」,与CountDownLatch比较像,但它侧重于工作本身,即指定的若干个工作都满足考核标准(某个屏障)之后,才能继续进行下面的工作,且可反复使用。这有点像一个研发团队,只有当所有人都确认完设计原型后,才能开始进入开发;只有全部开发完成之后,才能开始进行系统集成测试等等;

4、Exchanger:用于线程之间交换数据,更形象地说法是「交换机」,即当一个线程完成某项工作后想与另一个线程交换数据,就可以使用这个工具类来实现。

下面来一个个地演示它们的用法。


一、CountDownLatch

CountDownLatch的功能如果用图来表示的话,它是这样的:

Java多线程(7):JUC(上)_多线程_02


T1~T5这5个线程需要同时准备好之后主线程才能继续执行后续动作,那么此时程序就会挨个询问T1~T5:准备好了吗?只有准备OK了才能进行。

CountDownLatch实例效果的代码:

/**
* 发令枪
*
* @author 湘王
*/
public class CountDownLatchTester implements Runnable {
static final CountDownLatch latch = new CountDownLatch(10);
@Override
public void run() {
// 检查任务
try {
System.out.println(Thread.currentThread().getName() + " 检查完毕!");
} catch (Exception e) {
e.printStackTrace();
} finally {
latch.countDown();
}
}

public static void main(String[] args) throws InterruptedException {
ExecutorService executor = Executors.newFixedThreadPool(5);
for (int i = 10; i > 0; i--) {
Thread.sleep(1000);
executor.submit(new CountDownLatchTester());
System.out.println(i);
}

Thread.sleep(1000);
// 检查
latch.await();

System.out.println();
System.out.println("点火,发射!");
// 关闭线程池
executor.shutdown();
}
}


执行CountDownLatch后的效果是:

Java多线程(7):JUC(上)_Java_03


二、Semaphore

Semaphore的功能如果用图来表示的话,是这样的:

Java多线程(7):JUC(上)_Java_04


可以看到10个线程每次只能「放行」3个通过(最后一个自己过),是不是非常像红绿灯或摇号器?

Semaphore实例效果的代码:

/**
* 信号量(摇号器)
*
* @author 湘王
*/
public class SemaphoreTester implements Runnable {
static final Semaphore semaphore = new Semaphore(3);

@Override
public void run() {
try {
semaphore.acquire();
System.out.println(Thread.currentThread().getName() + " 开始进餐");
Thread.sleep(1000);
} catch (Exception e) {
e.printStackTrace();
}
semaphore.release();
}

public static void main(String[] args) {
ExecutorService excutor = Executors.newFixedThreadPool(15);
for (int i = 0; i < 15; i++) {
excutor.submit(new SemaphoreTester());
}
excutor.shutdown();
}
}


Semaphore执行后的效果是:

Java多线程(7):JUC(上)_JUC_05


三、CyclicBarrier

CyclicBarrier的功能如果用图来表示的话,是这样的:

Java多线程(7):JUC(上)_JUC_06


CyclicBarrier实例效果代码:

/**
* 栅栏
*
* @author 湘王
*/
public class CyclicBarrierTester implements Runnable {
private final static CyclicBarrier barrier = new CyclicBarrier(3);

@Override
public void run() {
try {
Thread.sleep(1000);
System.out.println(Thread.currentThread().getName() + " 已达到预定位置,等待指令...");
// 只有最后一个线程执行后,所有的线程才能执行2
barrier.await();
Thread.sleep(1000);
// 2 所有线程都会执行的动作
System.out.println(Thread.currentThread().getName() + " 已突破第一道封锁线");
} catch (Exception e) {
e.printStackTrace();
}
}

public static void main(String[] args) {
ExecutorService executor = Executors.newFixedThreadPool(3);
for (int i = 0; i < 15; i++) {
executor.submit(new CyclicBarrierTester());
}
// 关闭线程池
executor.shutdown();
}
}


CyclicBarrier执行后的效果是(因为这里是静态图片,无法看出每次执行三个的效果,感兴趣的话可以自己敲代码试试看):

Java多线程(7):JUC(上)_多线程_07


四、Exchanger

Exchanger的功能如果用图来表示的话,是这样的:

Java多线程(7):JUC(上)_Java_08



Exchanger实例代码:

/**
* 交换机
*
* @author 湘王
*/
public class ExchangerTester implements Runnable {
Exchanger<Object> exchanger = null;
Object object = null;

public ExchangerTester(Exchanger<Object> exchanger, Object object) {
this.exchanger = exchanger;
this.object = object;
}

@Override
public void run() {
try {
Object previous = this.object;
this.object = this.exchanger.exchange(this.object);
System.out.println(Thread.currentThread().getName() + " 用对象 " + previous + " 换对象 " + this.object);
} catch (InterruptedException e) {
e.printStackTrace();
}
}

public static void main(String[] args) {
Exchanger<Object> exchanger = new Exchanger<Object>();
ExchangerTester tester1 = new ExchangerTester(exchanger, "A");
ExchangerTester tester2 = new ExchangerTester(exchanger, "B");

new Thread(tester1).start();
new Thread(tester2).start();
}
}


Exchanger执行后的效果是:

Java多线程(7):JUC(上)_多线程_09


把这四种同步器掌握好(包括它们的组合使用),几乎可以解决90%以上的使用多线程的场景问题,再也不用担心不会多线程了。





感谢您的大驾光临!咨询技术、产品、运营和管理相关问题,请关注后留言。欢迎骚扰,不胜荣幸~


特别声明:以上内容(图片及文字)均为互联网收集或者用户上传发布,本站仅提供信息存储服务!如有侵权或有涉及法律问题请联系我们。
举报
评论区(0)
按点赞数排序
用户头像
精选文章
thumb 中国研究员首次曝光美国国安局顶级后门—“方程式组织”
thumb 俄乌线上战争,网络攻击弥漫着数字硝烟
thumb 从网络安全角度了解俄罗斯入侵乌克兰的相关事件时间线