JUC 常用 4 大并发工具类(1)

文章详细介绍了CyclicBarrier、CountDownLatch和Semaphore在Java并发编程中的使用,特别是如何通过Semaphore实现数据库连接池的流量控制,展示了它们在控制线程同步和资源分配方面的区别。
摘要由CSDN通过智能技术生成

private static class CollectThread implements Runnable {

@Override

public void run() {

StringBuffer result = new StringBuffer();

for (Map.Entry<String, Long> workResult : resultMap.entrySet()) {

result.append(“[” + workResult.getValue() + “]”);

}

System.out.println("the result = " + result);

System.out.println(“do other business…”);

}

}

/**

  • 工作子线程

  • 用于CyclicBarrier的一组线程

*/

private static class SubThread implements Runnable {

@Override

public void run() {

// 获取当前线程的ID

long id = Thread.currentThread().getId();

// 放入统计容器中

resultMap.put(String.valueOf(id), id);

Random random = new Random();

try {

if (random.nextBoolean()) {

Thread.sleep(1000 + id);

System.out.println(“Thread_”+id+“… do something”);

}

System.out.println(id+" is await");

cyclicBarrier.await();

Thread.sleep(1000+id);

System.out.println(“Thread_”+id+“…do its business”);

} catch (InterruptedException e) {

e.printStackTrace();

} catch (BrokenBarrierException e) {

e.printStackTrace();

}

}

}

public static void main(String[] args) {

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

Thread thread = new Thread(new SubThread());

thread.start();

}

}

}

返回结果:

11 is await

14 is await

15 is await

Thread_12… do something

12 is await

Thread_13… do something

13 is await

the result = [11][12][13][14][15]

do other business…

Thread_11…do its business

Thread_12…do its business

Thread_13…do its business

Thread_14…do its business

Thread_15…do its business

通过返回结果可以看出前面的11 14 15三个线程没有进入if语句块,在执行到await的时候进入了等待,而另外12 13两个线程进入到了if语句块当中,多休眠了1秒多,然后当5个线程同时到达await的时候,屏障开放,执行了barrierAction线程,然后线程组继续执行

解释一下CountDownLatch和CyclicBarrier的却别吧!

首先就是CountDownLatch的构造参数传入的数量一般都是大于等于线程,数量的,因为他是有第三方控制的,可以扣减多次,然后就是CyclicBarrier的构造参数第一个参数传入的数量一定是等于线程的个数的,因为他是由一组线程自身控制的

区别

CountDownLatch  CyclicBarrier

控制   第三方控制     自身控制

传入数量  大于等于线程数量 等于线程数量

Semaphore:

==========

Semaphore,俗称信号量,作用于控制同时访问某个特定资源的线程数量,用在流量控制

一说特定资源控制,那么第一时间就想到了数据库连接…

之前用等待超时模式写了一个数据库连接池,打算用这个Semaphone也写一个

/**

  • Creates a {@code Semaphore} with the given number of

  • permits and nonfair fairness setting.

  • @param permits the initial number of permits available.

  •    This value may be negative, in which case releases
    
  •    must occur before any acquires will be granted.
    

*/

public Semaphore(int permits) {

sync = new NonfairSync(permits);

}

在源码中可以看到在构建Semaphore信号量的时候,需要传入许可证的数量,这个数量就是资源的最大允许的访问的线程数

接下里用信号量实现一个数据库连接池

连接对象

package org.dance.day2.util.pool;

import org.dance.tools.SleepTools;

import java.sql.*;

import java.util.Map;

import java.util.Properties;

import java.util.concurrent.Executor;

/**

  • 数据库连接

  • @author ZYGisComputer

*/

public class SqlConnection implements Connection {

/**

  • 获取数据库连接

  • @return

*/

public static final Connection fetchConnection(){

return new SqlConnection();

}

@Override

public void commit() throws SQLException {

SleepTools.ms(70);

}

@Override

public Statement createStatement() throws SQLException {

SleepTools.ms(1);

return null;

}

@Override

public PreparedStatement prepareStatement(String sql) throws SQLException {

return null;

}

@Override

public CallableStatement prepareCall(String sql) throws SQLException {

return null;

}

@Override

public String nativeSQL(String sql) throws SQLException {

return null;

}

@Override

public void setAutoCommit(boolean autoCommit) throws SQLException {

}

@Override

public boolean getAutoCommit() throws SQLException {

return false;

}

@Override

public void rollback() throws SQLException {

}

@Override

public void close() throws SQLException {

}

@Override

public boolean isClosed() throws SQLException {

return false;

}

@Override

public DatabaseMetaData getMetaData() throws SQLException {

return null;

}

@Override

public void setReadOnly(boolean readOnly) throws SQLException {

}

@Override

public boolean isReadOnly() throws SQLException {

return false;

}

@Override

public void setCatalog(String catalog) throws SQLException {

}

@Override

public String getCatalog() throws SQLException {

return null;

}

@Override

public void setTransactionIsolation(int level) throws SQLException {

}

@Override

public int getTransactionIsolation() throws SQLException {

return 0;

}

@Override

public SQLWarning getWarnings() throws SQLException {

return null;

}

@Override

public void clearWarnings() throws SQLException {

}

@Override

public Statement createStatement(int resultSetType, int resultSetConcurrency) throws SQLException {

return null;

}

@Override

public PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency) throws SQLException {

return null;

}

@Override

public CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurrency) throws SQLException {

return null;

}

@Override

public Map<String, Class<?>> getTypeMap() throws SQLException {

return null;

}

@Override

public void setTypeMap(Map<String, Class<?>> map) throws SQLException {

}

@Override

public void setHoldability(int holdability) throws SQLException {

}

@Override

public int getHoldability() throws SQLException {

return 0;

}

@Override

public Savepoint setSavepoint() throws SQLException {

return null;

}

@Override

public Savepoint setSavepoint(String name) throws SQLException {

return null;

}

@Override

public void rollback(Savepoint savepoint) throws SQLException {

}

@Override

public void releaseSavepoint(Savepoint savepoint) throws SQLException {

}

@Override

public Statement createStatement(int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException {

return null;

}

@Override

public PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException {

return null;

}

@Override

public CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException {

return null;

}

@Override

public PreparedStatement prepareStatement(String sql, int autoGeneratedKeys) throws SQLException {

return null;

}

@Override

public PreparedStatement prepareStatement(String sql, int[] columnIndexes) throws SQLException {

return null;

}

@Override

public PreparedStatement prepareStatement(String sql, String[] columnNames) throws SQLException {

return null;

}

@Override

public Clob createClob() throws SQLException {

return null;

}

@Override

public Blob createBlob() throws SQLException {

return null;

}

@Override

public NClob createNClob() throws SQLException {

return null;

}

@Override

public SQLXML createSQLXML() throws SQLException {

return null;

}

@Override

public boolean isValid(int timeout) throws SQLException {

return false;

}

@Override

public void setClientInfo(String name, String value) throws SQLClientInfoException {

}

@Override

public void setClientInfo(Properties properties) throws SQLClientInfoException {

}

@Override

public String getClientInfo(String name) throws SQLException {

return null;

}

@Override

public Properties getClientInfo() throws SQLException {

return null;

}

@Override

public Array createArrayOf(String typeName, Object[] elements) throws SQLException {

return null;

}

@Override

public Struct createStruct(String typeName, Object[] attributes) throws SQLException {

return null;

}

@Override

public void setSchema(String schema) throws SQLException {

}

@Override

public String getSchema() throws SQLException {

return null;

}

@Override

public void abort(Executor executor) throws SQLException {

}

@Override

public void setNetworkTimeout(Executor executor, int milliseconds) throws SQLException {

}

@Override

public int getNetworkTimeout() throws SQLException {

return 0;

}

@Override

public T unwrap(Class iface) throws SQLException {

return null;

}

@Override

public boolean isWrapperFor(Class<?> iface) throws SQLException {

return false;

}

}

连接池对象

package org.dance.day2.util.pool;

import java.sql.Connection;

import java.util.ArrayList;

import java.util.HashSet;

import java.util.Iterator;

import java.util.LinkedList;

import java.util.concurrent.Semaphore;

/**

  • 使用信号量控制数据库的链接和释放

  • @author ZYGisComputer

*/

public class DBPoolSemaphore {

/**

  • 池容量

*/

private final static int POOL_SIZE = 10;

/**

  • useful 代表可用连接

  • useless 代表已用连接

  • 为什么要使用两个Semaphore呢?是因为,在连接池中不只有连接本身是资源,空位也是资源,也需要记录

*/

private final Semaphore useful, useless;

/**

  • 连接池

*/

private final static LinkedList POOL = new LinkedList<>();

/**

  • 使用静态块初始化池

*/

static {

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

POOL.addLast(SqlConnection.fetchConnection());

}

}

public DBPoolSemaphore() {

// 初始可用的许可证等于池容量

useful = new Semaphore(POOL_SIZE);

// 初始不可用的许可证容量为0

useless = new Semaphore(0);

}

/**

  • 获取数据库连接

  • @return 连接对象

*/

public Connection takeConnection() throws InterruptedException {

// 可用许可证减一

useful.acquire();

Connection connection;

synchronized (POOL) {

connection = POOL.removeFirst();

}

// 不可用许可证数量加一

useless.release();

return connection;

}

/**

  • 释放链接

  • @param connection 连接对象

*/

public void returnConnection(Connection connection) throws InterruptedException {

if(null!=connection){

// 打印日志

System.out.println(“当前有”+useful.getQueueLength()+“个线程等待获取连接,”

+“可用连接有”+useful.availablePermits()+“个”);

// 不可用许可证减一

useless.acquire();

synchronized (POOL){

POOL.addLast(connection);

}

// 可用许可证加一

useful.release();

}

}

}

测试类:

package org.dance.day2.util.pool;

import org.dance.tools.SleepTools;

import java.sql.Connection;

import java.util.Random;

/**

  • 测试Semaphore

  • @author ZYGisComputer

*/

public class UseSemaphore {

/**

  • 连接池

*/

public static final DBPoolSemaphore pool = new DBPoolSemaphore();

private static class BusiThread extends Thread{

@Override

public void run() {

// 随机数工具类 为了让每个线程持有连接的时间不一样

Random random = new Random();

long start = System.currentTimeMillis();

try {

Connection connection = pool.takeConnection();

System.out.println(“Thread_”+Thread.currentThread().getId()+

“_获取数据库连接耗时[”+(System.currentTimeMillis()-start)+“]ms.”);

// 模拟使用连接查询数据

SleepTools.ms(100+random.nextInt(100));

System.out.println(“查询数据完成归还连接”);

pool.returnConnection(connection);

} catch (InterruptedException e) {

e.printStackTrace();

}

}

}

public static void main(String[] args) {

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

BusiThread busiThread = new BusiThread();

busiThread.start();

}

}

}

测试返回结果:

Thread_11_获取数据库连接耗时[0]ms.

Thread_12_获取数据库连接耗时[0]ms.

Thread_13_获取数据库连接耗时[0]ms.

Thread_14_获取数据库连接耗时[0]ms.

Thread_15_获取数据库连接耗时[0]ms.

Thread_16_获取数据库连接耗时[0]ms.

Thread_17_获取数据库连接耗时[0]ms.

Thread_18_获取数据库连接耗时[0]ms.

Thread_19_获取数据库连接耗时[0]ms.

Thread_20_获取数据库连接耗时[0]ms.

查询数据完成归还连接

当前有40个线程等待获取连接,可用连接有0个

Thread_21_获取数据库连接耗时[112]ms.

查询数据完成归还连接

查询数据完成归还连接

当前有2个线程等待获取连接,可用连接有0个

Thread_59_获取数据库连接耗时[637]ms.

查询数据完成归还连接

当前有1个线程等待获取连接,可用连接有0个

Thread_60_获取数据库连接耗时[660]ms.

查询数据完成归还连接

当前有0个线程等待获取连接,可用连接有0个

查询数据完成归还连接

当前有0个线程等待获取连接,可用连接有8个

查询数据完成归还连接

当前有0个线程等待获取连接,可用连接有9个

通过执行结果可以很明确的看到,一上来就有10个线程获取到了连接,然后后面的40个线程进入阻塞,然后只有释放链接之后,等待的线程就会有一个拿到,然后越后面的线程等待的时间就越长,然后一直到所有的线程执行完毕

最后打印的可用连接有九个不是因为少了一个是因为在释放之前打印的,不是错误

从结果中可以看到,我们对连接池中的资源的到了控制,这就是信号量的流量控制

Exchanger:

==========

Exchanger,俗称交换器,用于在线程之间交换数据,但是比较受限,因为只能两个线程之间交换数据

/**

  • Creates a new Exchanger.

*/

public Exchanger() {

participant = new Participant();

}

这个构造函数没有什么好说的,也没有入参,只有在创建的时候指定一下需要交换的数据的泛型即可,下面看代码

package org.dance.day2.util;

import java.util.HashSet;

import java.util.Set;

import java.util.concurrent.Exchanger;

/**

  • 线程之间交换数据

  • @author ZYGisComputer

*/

public class UseExchange {

private static final Exchanger<Set> exchanger = new Exchanger<>();

public static void main(String[] args) {

new Thread(){

@Override

public void run() {

Set aSet = new HashSet<>();

aSet.add(“A”);

aSet.add(“B”);

aSet.add(“C”);

try {

Set exchange = exchanger.exchange(aSet);

for (String s : exchange) {

System.out.println(“aSet”+s);

}

} catch (InterruptedException e) {

e.printStackTrace();

}

}

}.start();

new Thread(){

@Override

public void run() {

Set bSet = new HashSet<>();

bSet.add(“1”);

bSet.add(“2”);

bSet.add(“3”);

try {

Set exchange = exchanger.exchange(bSet);

for (String s : exchange) {

System.out.println(“bSet”+s);

}

} catch (InterruptedException e) {

e.printStackTrace();

}

}

}.start();

}

}

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

深知大多数Java工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

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

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,真正体系化!

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

如果你觉得这些内容对你有帮助,可以扫码获取!!(备注Java获取)

img

最后

Java架构进阶面试及知识点文档笔记

这份文档共498页,其中包括Java集合,并发编程,JVM,Dubbo,Redis,Spring全家桶,MySQL,Kafka等面试解析及知识点整理

image

Java分布式高级面试问题解析文档

其中都是包括分布式的面试问题解析,内容有分布式消息队列,Redis缓存,分库分表,微服务架构,分布式高可用,读写分离等等!

image

互联网Java程序员面试必备问题解析及文档学习笔记

image

Java架构进阶视频解析合集
《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!
exchanger.exchange(bSet);

for (String s : exchange) {

System.out.println(“bSet”+s);

}

} catch (InterruptedException e) {

e.printStackTrace();

}

}

}.start();

}

}

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

深知大多数Java工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。[外链图片转存中…(img-wndujOZp-1713334885915)]

[外链图片转存中…(img-zuHaV4ni-1713334885916)]

[外链图片转存中…(img-Q1AweEt7-1713334885916)]

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,真正体系化!

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

如果你觉得这些内容对你有帮助,可以扫码获取!!(备注Java获取)

img

最后

Java架构进阶面试及知识点文档笔记

这份文档共498页,其中包括Java集合,并发编程,JVM,Dubbo,Redis,Spring全家桶,MySQL,Kafka等面试解析及知识点整理

[外链图片转存中…(img-InUISY4s-1713334885916)]

Java分布式高级面试问题解析文档

其中都是包括分布式的面试问题解析,内容有分布式消息队列,Redis缓存,分库分表,微服务架构,分布式高可用,读写分离等等!

[外链图片转存中…(img-rN8eNQuw-1713334885916)]

互联网Java程序员面试必备问题解析及文档学习笔记

[外链图片转存中…(img-4n5juc75-1713334885917)]

Java架构进阶视频解析合集
《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值