Java并发编程中常见的并发工具类

Thread-1 赶上了1趟班车。

Thread-10 赶上了1趟班车。

Thread-20 赶上了1趟班车。

Thread-81 赶上了1趟班车。

Thread-98 赶上了1趟班车。

Thread-95 赶上了1趟班车。

Thread-41 赶上了1趟班车。

Thread-82 赶上了1趟班车。

Thread-56 赶上了1趟班车。

Thread-37 赶上了1趟班车。

Thread-74 赶上了1趟班车。

Thread-2 赶上了1趟班车。

车上座位已满,请等待下一班!

Thread-12 赶上了2趟班车。

Thread-13 赶上了2趟班车。

Thread-68 赶上了2趟班车。

Thread-80 赶上了2趟班车。

Thread-40 赶上了2趟班车。

Thread-39 赶上了2趟班车。

Thread-14 赶上了2趟班车。

Thread-87 赶上了2趟班车。

Thread-76 赶上了2趟班车。

Thread-28 赶上了2趟班车。

Thread-26 赶上了2趟班车。

Thread-51 赶上了2趟班车。

Thread-19 赶上了2趟班车。

Thread-32 赶上了2趟班车。

Thread-22 赶上了2趟班车。

Thread-34 赶上了2趟班车。

Thread-52 赶上了2趟班车。

Thread-9 赶上了2趟班车。

Thread-55 赶上了2趟班车。

Thread-78 赶上了2趟班车。

车上座位已满,请等待下一班!

Thread-23 赶上了3趟班车。

Thread-94 赶上了3趟班车。

Thread-3 赶上了3趟班车。

Thread-4 赶上了3趟班车。

Thread-11 赶上了3趟班车。

Thread-54 赶上了3趟班车。

Thread-30 赶上了3趟班车。

Thread-46 赶上了3趟班车。

Thread-33 赶上了3趟班车。

Thread-61 赶上了3趟班车。

Thread-58 赶上了3趟班车。

Thread-47 赶上了3趟班车。

Thread-65 赶上了3趟班车。

Thread-72 赶上了3趟班车。

Thread-5 赶上了3趟班车。

Thread-96 赶上了3趟班车。

Thread-92 赶上了3趟班车。

Thread-88 赶上了3趟班车。

Thread-18 赶上了3趟班车。

Thread-83 赶上了3趟班车。

车上座位已满,请等待下一班!

Thread-7 赶上了4趟班车。

Thread-64 赶上了4趟班车。

Thread-90 赶上了4趟班车。

Thread-62 赶上了4趟班车。

Thread-17 赶上了4趟班车。

Thread-49 赶上了4趟班车。

Thread-16 赶上了4趟班车。

Thread-45 赶上了4趟班车。

Thread-38 赶上了4趟班车。

Thread-15 赶上了4趟班车。

Thread-50 赶上了4趟班车。

Thread-66 赶上了4趟班车。

Thread-59 赶上了4趟班车。

Thread-25 赶上了4趟班车。

Thread-35 赶上了4趟班车。

Thread-43 赶上了4趟班车。

Thread-8 赶上了4趟班车。

Thread-77 赶上了4趟班车。

Thread-97 赶上了4趟班车。

Thread-53 赶上了4趟班车。

车上座位已满,请等待下一班!

Thread-27 赶上了5趟班车。

Thread-21 赶上了5趟班车。

Thread-71 赶上了5趟班车。

Thread-75 赶上了5趟班车。

Thread-85 赶上了5趟班车。

Thread-73 赶上了5趟班车。

Thread-67 赶上了5趟班车。

Thread-91 赶上了5趟班车。

Thread-79 赶上了5趟班车。

Thread-44 赶上了5趟班车。

Thread-36 赶上了5趟班车。

Thread-24 赶上了5趟班车。

Thread-0 赶上了5趟班车。

Thread-60 赶上了5趟班车。

Thread-70 赶上了5趟班车。

Thread-31 赶上了5趟班车。

Thread-63 赶上了5趟班车。

Thread-29 赶上了5趟班车。

Thread-42 赶上了5趟班车。

Thread-86 赶上了5趟班车。

CyclicBarrier 就像一个屏障,实例化的时候需要传入两个参数,第一个参数指定我们的屏障最多拦截多少个线程后就打开屏障,第二个参数指明最后一个到达屏障的线程需要额外做的操作。

一般而言,最后一个线程到达屏障后,屏障将会打开,释放前面所有的线程,并在最后重新关上屏障。

CyclicBarrier 只需要用到一个 await 就可以完成所有的功能,我们总结下该方法的实现逻辑:

1、首先,减少一次可用资源数量。

2、如果可用资源数为零,则说明自己是最后一个线程,于是会执行我们传入的额外操作,唤醒所有已经到达在等待的线程,并重新开启一个屏障计数。

3、否则说明自己不是最后一个线程,于是将自身线程在一个循环当中阻塞到一个条件队列上。

三、信号量 Semaphore


Semaphore又名信号量,是操作系统中的一个概念,在Java并发编程中,信号量控制的是线程并发的数量。

作用:Semaphore管理一系列许可证。每个acquire方法阻塞,直到有一个许可证可以获得然后拿走一个许可证;每个release方法增加一个许可证,这可能会释放一个阻塞的acquire方法。然而,其实并没有实际的许可证这个对象,Semaphore只是维持了一个可获得许可证的数量,主要控制同时访问某个特定资源的线程数量,多用在流量控制。

注意:其他Semaphore的底层实现就是基于AQS的共享锁实现的。

如果一个线程要访问共享资源,必须先获得信号量,如果信号量的计数器值大于1,意味

着有共享资源可以访问,则使其计数器值减去1,再访问共享资源。如果计数器值为0,线

程进入休眠。当某个线程使用完共享资源后,释放信号量,并将信号量内部的计数器加1,之前进入休眠的线程将被唤醒并再次试图获得信号量。

Semaphore 适用于什么样的使用场景呢,我们举个通俗的例子:

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

package com.concurrent.util;

import java.util.concurrent.ExecutorService;

import java.util.concurrent.Executors;

import java.util.concurrent.Semaphore;

/**

  • @author riemann

  • @date 2019/07/27 23:31

*/

public class SemaphoreDemo {

private static final int THREAD_COUNT = 30;

private static ExecutorService threadPool = Executors.newFixedThreadPool(THREAD_COUNT);

private static Semaphore semaphore = new Semaphore(10);

public static void main(String[] args) {

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

threadPool.execute(new Runnable() {

@Override

public void run() {

try {

semaphore.acquire();

System.out.println(Thread.currentThread().getName() + " 连接成功,保存数据。");

Thread.sleep((long) (Math.random() * 1000));

System.out.println(Thread.currentThread().getName() + " 释放连接。");

semaphore.release();

} catch (InterruptedException e) {

e.printStackTrace();

}

}

});

}

threadPool.shutdown();

}

}

输出结果:

pool-1-thread-2 连接成功,保存数据。

pool-1-thread-3 连接成功,保存数据。

pool-1-thread-1 连接成功,保存数据。

pool-1-thread-4 连接成功,保存数据。

pool-1-thread-6 连接成功,保存数据。

pool-1-thread-7 连接成功,保存数据。

pool-1-thread-8 连接成功,保存数据。

pool-1-thread-10 连接成功,保存数据。

pool-1-thread-5 连接成功,保存数据。

pool-1-thread-9 连接成功,保存数据。

pool-1-thread-10 释放连接。

pool-1-thread-14 连接成功,保存数据。

pool-1-thread-2 释放连接。

pool-1-thread-15 连接成功,保存数据。

pool-1-thread-9 释放连接。

pool-1-thread-12 连接成功,保存数据。

pool-1-thread-8 释放连接。

pool-1-thread-16 连接成功,保存数据。

pool-1-thread-3 释放连接。

pool-1-thread-11 连接成功,保存数据。

pool-1-thread-14 释放连接。

pool-1-thread-18 连接成功,保存数据。

pool-1-thread-18 释放连接。

pool-1-thread-19 连接成功,保存数据。

pool-1-thread-7 释放连接。

pool-1-thread-20 连接成功,保存数据。

pool-1-thread-5 释放连接。

pool-1-thread-17 连接成功,保存数据。

pool-1-thread-6 释放连接。

pool-1-thread-13 连接成功,保存数据。

pool-1-thread-13 释放连接。

pool-1-thread-21 连接成功,保存数据。

pool-1-thread-1 释放连接。

pool-1-thread-22 连接成功,保存数据。

pool-1-thread-4 释放连接。

pool-1-thread-23 连接成功,保存数据。

pool-1-thread-12 释放连接。

pool-1-thread-24 连接成功,保存数据。

pool-1-thread-22 释放连接。

pool-1-thread-25 连接成功,保存数据。

pool-1-thread-19 释放连接。

pool-1-thread-26 连接成功,保存数据。

pool-1-thread-17 释放连接。

pool-1-thread-27 连接成功,保存数据。

pool-1-thread-15 释放连接。

pool-1-thread-28 连接成功,保存数据。

pool-1-thread-11 释放连接。

pool-1-thread-30 连接成功,保存数据。

pool-1-thread-26 释放连接。

pool-1-thread-29 连接成功,保存数据。

pool-1-thread-24 释放连接。

pool-1-thread-16 释放连接。

pool-1-thread-28 释放连接。

pool-1-thread-29 释放连接。

pool-1-thread-20 释放连接。

pool-1-thread-27 释放连接。

pool-1-thread-21 释放连接。

pool-1-thread-25 释放连接。

pool-1-thread-30 释放连接。

pool-1-thread-23 释放连接。

在代码中,虽然有30个线程在执行,但是只允许10个并发执行。Semaphore的构造方法Semaphore(int permits)接受一个整型的数字,表示可用的许可证数量。Semaphore(10)表示允许10个线程获取许可证,也就是最大并发数是10。

Semaphore的用法也很简单,首先线程使用Semaphore的acquire()方法获取一个许可证,使用完之后调用release()方法归还许可证。还可以用tryAcquire()方法尝试获取许可证。

Semaphore还提供一些其他方法,具体如下:

int availablePermits():返回此信号量中当前可用的许可证数。

int getQueueLength():返回正在等待获取许可证的线程数。

boolean hasQueuedThreads():是否有线程正在等待获取许可证。

void reducePermits(int reduction):减少reduction个许可证,是个protected方法。

Collection getQueuedThreads():返回所有等待获取许可证的线程集合,是个protected方法。

四、线程间交换数据的 Exchanger


Exchanger(交换者)是一个用于线程间协作的工具类。Exchanger用于进行线程间的数据交换。它提供一个同步点,在这个同步点,两个线程可以交换彼此的数据。这两个线程通过exchange方法交换数据,如果第一个线程先执行exchange()方法,它会一直等待第二个线程也执行exchange方法,当两个线程都到达同步点时,这两个线程就可以交换数据,将本线程生产出来的数据传递给对方。

下面来看一下Exchanger的应用场景:

1、Exchanger可以用于遗传算法,遗传算法里需要选出两个人作为交配对象,这时候会交换两人的数据,并使用交叉规则得出2个交配结果。

2、Exchanger也可以用于校对工作,比如我们需要将纸制银行流水通过人工的方式录入成电子银行流水,为了避免错误,采用AB岗两人进行录入,录入到Excel之后,系统需要加载这两个Excel,并对两个Excel数据进行校对,看看是否录入一致。

package com.concurrent.util;

import java.util.concurrent.Exchanger;

import java.util.concurrent.ExecutorService;

import java.util.concurrent.Executors;

/**

  • @author riemann

  • @date 2019/07/27 23:49

*/

public class ExchangerDemo {

private static final Exchanger exchanger = new Exchanger();

private static ExecutorService threadPool = Executors.newFixedThreadPool(2);

public static void main(String[] args) {

threadPool.execute(new Runnable() {

@Override

public void run() {

try {

String A = “银行流水100”;// A录入银行流水数据

String B = exchanger.exchange(A);

System.out.println("A的视角:A和B数据是否一致: " + A.equals(B) +

",A录入的是: " + A + ",B录入是: " + B + “。”);

} catch (InterruptedException e) {

e.printStackTrace();

}

}

});

threadPool.execute(new Runnable() {

@Override

public void run() {

try {

String B = “银行流水200”;// B录入银行流水数据

String A = exchanger.exchange(B);

System.out.println("B的视角:A和B数据是否一致: " + A.equals(B) +

",A录入的是: " + A + ",B录入是: " + B + “。”);

} catch (InterruptedException e) {

e.printStackTrace();

}

}

});

}

}

输出结果:

B的视角:A和B数据是否一致: false,A录入的是: 银行流水100,B录入是: 银行流水200。

A的视角:A和B数据是否一致: false,A录入的是: 银行流水100,B录入是: 银行流水200。

如果两个线程有一个没有执行exchange()方法,则会一直等待,如果担心有特殊情况发生,避免一直等待,可以使用exchange(V x,longtimeout,TimeUnit unit)设置最大等待时长。

五、CyclicBarrier和CountDownLatch的区别


第一个区别:

最后

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数Java工程师,想要提升技能,往往是自己摸索成长,自己不成体系的自学效果低效漫长且无助。

因此收集整理了一份《2024年Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,不论你是刚入门Java开发的新手,还是希望在技术上不断提升的资深开发者,这些资料都将为你打开新的学习之门!

如果你觉得这些内容对你有帮助,需要这份全套学习资料的朋友可以戳我获取!!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

第一个区别:

最后

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数Java工程师,想要提升技能,往往是自己摸索成长,自己不成体系的自学效果低效漫长且无助。

因此收集整理了一份《2024年Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。

[外链图片转存中…(img-DDPqfBex-1715737360005)]

[外链图片转存中…(img-YhuABgb2-1715737360005)]

[外链图片转存中…(img-ICoOW7TX-1715737360006)]

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,不论你是刚入门Java开发的新手,还是希望在技术上不断提升的资深开发者,这些资料都将为你打开新的学习之门!

如果你觉得这些内容对你有帮助,需要这份全套学习资料的朋友可以戳我获取!!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

  • 4
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值