JUC常用并发工具类

news2024/11/25 6:48:55

JUC常用并发工具类

1、什么是JUC?

JUC 就是 java.util.concurrent 包,这个包俗称 JUC,里面都是解决并发问题的一些东西,该包的位置位于 java 下

面的 rt.jar 包下面。

2、4大常用并发工具类

2.1 CountDownLatch

CountDownLatch,俗称闭锁,作用是类似加强版的 Join,是让一组线程等待其他的线程完成工作以后才执行就

比如在启动框架服务的时候,我们主线程需要在环境线程初始化完成之后才能启动,这时候我们就可以实现使用

CountDownLatch 来完成。

public CountDownLatch(int count) {
    if (count < 0) 
        throw new IllegalArgumentException("count < 0");
    this.sync = new Sync(count);
}

在源码中可以看到,创建 CountDownLatch 时,需要传入一个 int 类型的参数,将决定在执行次扣减之后,等待

的线程被唤醒。

CountDownLatch 方法介绍:

  • CountDownLatch:初始化方法。

  • await:等待方法,同时带参数的是超时重载方法。

  • countDown:每执行一次,计数器减一,就是初始化传入的数字,也代表着一个线程完成了任务。

  • getCount:获取当前值。

  • toString:这个就不用说了。

CountDownLatch 里面的 Sync 是一个内部类,外面的方法其实都是操作这个内部类的,这个内部类继承了

AQS,实现的标准方法。

CountDownLatch 工作流程:
在这里插入图片描述

主线程中创建 CountDownLatch(3),然后主线程 await 阻塞,然后线程A,B,C各自完成了任务,调用了

countDown 之后,每个线程调用一次计数器就会减一,初始是3,然后A线程调用后变成2,B线程调用后变成1,

C线程调用后变成0,这时就会唤醒正在await的主线程,然后主线程继续执行。

休眠工具类:

package com.utils;

import java.util.concurrent.TimeUnit;

/**
 * 类说明:线程休眠辅助工具类
 */

public class SleepTools {

    /**
     * 按秒休眠
     *
     * @param seconds 秒数
     */
    public static final void second(int seconds) {
        try {
            TimeUnit.SECONDS.sleep(seconds);
        } catch (InterruptedException e) {
        }
    }

    /**
     * 按毫秒数休眠
     *
     * @param seconds 毫秒数
     */
    public static final void ms(int seconds) {
        try {
            TimeUnit.MILLISECONDS.sleep(seconds);
        } catch (InterruptedException e) {
        }
    }
}
package com.countdownlatch;

import com.utils.SleepTools;

import java.util.concurrent.CountDownLatch;

/**
 * CountDownLatch的使用,有五个线程,6个扣除点扣除完成后主线程和业务线程才能执行工作
 * 扣除点一般都是大于等于需要初始化的线程的
 */

public class UseCountDownLatch {
    /**
     * 设置为6个扣除点
     */
    static CountDownLatch countDownLatch = new CountDownLatch(6);

    /**
     * 初始化线程
     */
    private static class InitThread implements Runnable {
        @Override
        public void run() {
            System.out.println("thread_" + Thread.currentThread().getId() + " ready init work .....");
            // 执行扣减 扣减不代表结束
            countDownLatch.countDown();
            for (int i = 0; i < 2; i++) {
                System.out.println("thread_" + Thread.currentThread().getId() + ".....continue do its work");
            }
        }
    }

    /**
     * 业务线程
     */
    private static class BusiThread implements Runnable {
        @Override
        public void run() {
            // 业务线程需要在等初始化完毕后才能执行
            try {
                countDownLatch.await();
                for (int i = 0; i < 3; i++) {
                    System.out.println("BusiThread " + Thread.currentThread().getId() + " do business-----");
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    public static void main(String[] args) {
        // 创建单独的初始化线程
        new Thread() {
            @Override
            public void run() {
                SleepTools.ms(1);
                System.out.println("thread_" + Thread.currentThread().getId() + " ready init work step 1st.....");
                // 扣减一次
                countDownLatch.countDown();
                System.out.println("begin stop 2nd.....");
                SleepTools.ms(1);
                System.out.println("thread_" + Thread.currentThread().getId() + " ready init work step 2nd.....");
                // 扣减一次
                countDownLatch.countDown();
            }
        }.start();
        // 启动业务线程
        new Thread(new BusiThread()).start();
        // 启动初始化线程
        for (int i = 0; i <= 3; i++) {
            new Thread(new InitThread()).start();
        }
        // 主线程进入等待
        try {
            countDownLatch.await();
            System.out.println("Main do ites work.....");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

返回结果:

# 程序输出
thread_14 ready init work .....
thread_17 ready init work .....
thread_16 ready init work .....
thread_15 ready init work .....
thread_16.....continue do its work
thread_17.....continue do its work
thread_14.....continue do its work
thread_17.....continue do its work
thread_16.....continue do its work
thread_15.....continue do its work
thread_14.....continue do its work
thread_15.....continue do its work
thread_12 ready init work step 1st.....
begin stop 2nd.....
thread_12 ready init work step 2nd.....
Main do ites work.....
BusiThread 13 do business-----
BusiThread 13 do business-----
BusiThread 13 do business-----

通过返回结果就可以很直接的看到业务线程是在初始化线程完全跑完之后,才开始执行的,因为业务线程中包含

countDownLatch.await()。

2.2 CyclicBarrier

CyclicBarrier,俗称栅栏锁,作用是让一组线程到达某个屏障,被阻塞,一直到组内的最后一个线程到达,然

后屏障开放,接着,所有的线程继续运行。

这个感觉和 CountDownLatch 有点相似,但是其实是不一样的,所谓的差别,将在下面详解。

CyclicBarrier 的构造参数有两个:

public CyclicBarrier(int parties) {
    this(parties, null);
}

public CyclicBarrier(int parties, Runnable barrierAction) {
	if (parties <= 0) 
        throw new IllegalArgumentException();
	this.parties = parties;
    this.count = parties;
    this.barrierCommand = barrierAction;
}

很明显能感觉出来,上面的构造参数调用了下面的构造参数,是一个构造方法重载。

首先这个第一个参数也是 Int 类型的,传入的是执行线程的个数,这个数量和 CountDownLatch 不一样,这个数

量是需要和线程数量吻合的,CountDownLatch 则不一样,CountDownLatch 可以大于等于,而 CyclicBarrier 只

能等于,然后是第二个参数,第二个参数是 barrierAction,这个参数是当屏障开放后,执行的任务线程,如果当

屏障开放后需要执行什么任务,可以写在这个线程中。

在这里插入图片描述

主线程创建 CyclicBarrier(3,barrierAction),然后由线程开始执行,线程A,B执行完成后都调用了 await,然后

他们都在一个屏障前阻塞者,需要等待线程C也,执行完成,调用 await 之后,然后三个线程都达到屏障后,屏障

开放,然后线程继续执行,并且 barrierAction 在屏障开放的一瞬间也开始执行。

package com.cyclicbarrier;

import java.util.Map;
import java.util.Random;
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CyclicBarrier;

/**
 * CyclicBarrier的使用
 */
public class UseCyclicBarrier {

    /**
     * 存放子线程工作结果的安全容器
     */
    private static ConcurrentHashMap<String, Long> resultMap = new ConcurrentHashMap<>();

    private static CyclicBarrier cyclicBarrier = new CyclicBarrier(5, new CollectThread());

    /**
     * 结果打印线程
     * 用来演示CyclicBarrier的第二个参数,barrierAction
     */
    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();
        }
    }
}

返回结果:

# 程序输出
13 is await
14 is await
12 is await
Thread_15..... do something
15 is await
Thread_16..... do something
16 is await
the result = [12][13][14][15][16]
do other business.....
Thread_12.....do its business
Thread_13.....do its business
Thread_14.....do its business
Thread_15.....do its business
Thread_16.....do its business

通过返回结果可以看出前面的12 13 14三个线程没有进入if语句块,在执行到await的时候进入了等待,而另外15

16 两个线程进入到了if语句块当中,多休眠了1秒多,然后当5个线程同时到达await的时候,屏障开放,执行了

barrierAction 线程,然后线程组继续执行。

解释一下 CountDownLatch 和 CyclicBarrier 的区别:

首先就是 CountDownLatch 的构造参数传入的数量一般都是大于等于线程数量的,因为他是有第三方控制的,可

以扣减多次,然后就是CyclicBarrier的构造参数第一个参数传入的数量一定是等于线程的个数的,因为他是由一组

线程自身控制的。

区别CountDownLatchCyclicBarrier
控制第三方控制自身控制
传入数量大于等于线程数量等于线程数量

2.3 Semaphore

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

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

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

public Semaphore(int permits) {
	sync = new NonfairSync(permits);
}

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

问的线程数。接下里用信号量实现一个数据库连接池。

连接对象:

package com.semaphore;

import com.utils.SleepTools;

import java.sql.*;
import java.util.Map;
import java.util.Properties;
import java.util.concurrent.Executor;

/**
 * 数据库连接
 */
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> T unwrap(Class<T> iface) throws SQLException {
        return null;
    }

    @Override
    public boolean isWrapperFor(Class<?> iface) throws SQLException {
        return false;
    }
}

连接池对象:

package com.semaphore;

import java.sql.Connection;
import java.util.LinkedList;
import java.util.concurrent.Semaphore;

/**
 * 使用信号量控制数据库的链接和释放
 */
public class DBPoolSemaphore {

    /**
     * 池容量
     */
    private final static int POOL_SIZE = 10;

    /**
     * useful 代表可用连接
     * useless 代表已用连接
     * 为什么要使用两个Semaphore呢?是因为,在连接池中不只有连接本身是资源,空位也是资源,也需要记录
     */
    private final Semaphore useful, useless;

    /**
     * 连接池
     */
    private final static LinkedList<Connection> 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 com.semaphore;

import com.utils.SleepTools;

import java.sql.Connection;
import java.util.Random;

/**
 * 测试Semaphore
 */
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_12_获取数据库连接耗时[0]ms.
Thread_16_获取数据库连接耗时[0]ms.
Thread_13_获取数据库连接耗时[0]ms.
Thread_14_获取数据库连接耗时[0]ms.
Thread_15_获取数据库连接耗时[0]ms.
Thread_18_获取数据库连接耗时[0]ms.
Thread_17_获取数据库连接耗时[0]ms.
Thread_19_获取数据库连接耗时[0]ms.
Thread_42_获取数据库连接耗时[0]ms.
Thread_43_获取数据库连接耗时[0]ms.
查询数据完成归还连接
当前有40个线程等待获取连接,可用连接有0个
Thread_20_获取数据库连接耗时[107]ms.
查询数据完成归还连接
当前有39个线程等待获取连接,可用连接有0个
Thread_40_获取数据库连接耗时[107]ms.
查询数据完成归还连接
当前有38个线程等待获取连接,可用连接有0个
Thread_28_获取数据库连接耗时[127]ms.
查询数据完成归还连接
当前有37个线程等待获取连接,可用连接有0个
Thread_29_获取数据库连接耗时[130]ms.
查询数据完成归还连接
当前有36个线程等待获取连接,可用连接有0个
Thread_22_获取数据库连接耗时[134]ms.
查询数据完成归还连接
当前有35个线程等待获取连接,可用连接有0个
Thread_23_获取数据库连接耗时[153]ms.
查询数据完成归还连接
当前有34个线程等待获取连接,可用连接有0个
Thread_26_获取数据库连接耗时[159]ms.
查询数据完成归还连接
当前有33个线程等待获取连接,可用连接有0个
Thread_27_获取数据库连接耗时[174]ms.
查询数据完成归还连接
当前有32个线程等待获取连接,可用连接有0个
Thread_30_获取数据库连接耗时[179]ms.
查询数据完成归还连接
当前有31个线程等待获取连接,可用连接有0个
Thread_31_获取数据库连接耗时[185]ms.
查询数据完成归还连接
当前有30个线程等待获取连接,可用连接有0个
Thread_34_获取数据库连接耗时[214]ms.
查询数据完成归还连接
当前有29个线程等待获取连接,可用连接有0个
Thread_35_获取数据库连接耗时[238]ms.
查询数据完成归还连接
当前有28个线程等待获取连接,可用连接有0个
Thread_32_获取数据库连接耗时[244]ms.
查询数据完成归还连接
当前有27个线程等待获取连接,可用连接有0个
Thread_38_获取数据库连接耗时[260]ms.
查询数据完成归还连接
当前有26个线程等待获取连接,可用连接有0个
Thread_39_获取数据库连接耗时[270]ms.
查询数据完成归还连接
当前有25个线程等待获取连接,可用连接有0个
Thread_46_获取数据库连接耗时[295]ms.
查询数据完成归还连接
当前有24个线程等待获取连接,可用连接有0个
Thread_21_获取数据库连接耗时[322]ms.
查询数据完成归还连接
当前有23个线程等待获取连接,可用连接有0个
Thread_24_获取数据库连接耗时[333]ms.
查询数据完成归还连接
当前有22个线程等待获取连接,可用连接有0个
Thread_33_获取数据库连接耗时[357]ms.
查询数据完成归还连接
当前有21个线程等待获取连接,可用连接有0个
Thread_47_获取数据库连接耗时[367]ms.
查询数据完成归还连接
当前有20个线程等待获取连接,可用连接有0个
Thread_36_获取数据库连接耗时[370]ms.
查询数据完成归还连接
当前有19个线程等待获取连接,可用连接有0个
Thread_37_获取数据库连接耗时[374]ms.
查询数据完成归还连接
当前有18个线程等待获取连接,可用连接有0个
Thread_25_获取数据库连接耗时[392]ms.
查询数据完成归还连接
当前有17个线程等待获取连接,可用连接有0个
Thread_41_获取数据库连接耗时[404]ms.
查询数据完成归还连接
当前有16个线程等待获取连接,可用连接有0个
Thread_44_获取数据库连接耗时[416]ms.
查询数据完成归还连接
当前有15个线程等待获取连接,可用连接有0个
Thread_45_获取数据库连接耗时[433]ms.
查询数据完成归还连接
当前有14个线程等待获取连接,可用连接有0个
Thread_48_获取数据库连接耗时[469]ms.
查询数据完成归还连接
当前有13个线程等待获取连接,可用连接有0个
Thread_50_获取数据库连接耗时[482]ms.
查询数据完成归还连接
当前有12个线程等待获取连接,可用连接有0个
Thread_49_获取数据库连接耗时[514]ms.
查询数据完成归还连接
当前有11个线程等待获取连接,可用连接有0个
Thread_52_获取数据库连接耗时[523]ms.
查询数据完成归还连接
当前有10个线程等待获取连接,可用连接有0个
Thread_51_获取数据库连接耗时[533]ms.
查询数据完成归还连接
当前有9个线程等待获取连接,可用连接有0个
Thread_53_获取数据库连接耗时[540]ms.
查询数据完成归还连接
当前有8个线程等待获取连接,可用连接有0个
Thread_56_获取数据库连接耗时[561]ms.
查询数据完成归还连接
当前有7个线程等待获取连接,可用连接有0个
Thread_57_获取数据库连接耗时[574]ms.
查询数据完成归还连接
当前有6个线程等待获取连接,可用连接有0个
Thread_54_获取数据库连接耗时[591]ms.
查询数据完成归还连接
当前有5个线程等待获取连接,可用连接有0个
Thread_55_获取数据库连接耗时[605]ms.
查询数据完成归还连接
当前有4个线程等待获取连接,可用连接有0个
Thread_60_获取数据库连接耗时[615]ms.
查询数据完成归还连接
当前有3个线程等待获取连接,可用连接有0个
Thread_58_获取数据库连接耗时[663]ms.
查询数据完成归还连接
当前有2个线程等待获取连接,可用连接有0个
Thread_59_获取数据库连接耗时[667]ms.
查询数据完成归还连接
当前有1个线程等待获取连接,可用连接有0个
Thread_61_获取数据库连接耗时[677]ms.
查询数据完成归还连接
当前有0个线程等待获取连接,可用连接有0个
查询数据完成归还连接
当前有0个线程等待获取连接,可用连接有1个
查询数据完成归还连接
当前有0个线程等待获取连接,可用连接有2个
查询数据完成归还连接
当前有0个线程等待获取连接,可用连接有3个
查询数据完成归还连接
当前有0个线程等待获取连接,可用连接有4个
查询数据完成归还连接
当前有0个线程等待获取连接,可用连接有5个
查询数据完成归还连接
当前有0个线程等待获取连接,可用连接有6个
查询数据完成归还连接
当前有0个线程等待获取连接,可用连接有7个
查询数据完成归还连接
当前有0个线程等待获取连接,可用连接有8个
查询数据完成归还连接
当前有0个线程等待获取连接,可用连接有9个

通过执行结果可以很明确的看到,一上来就有10个线程获取到了连接,然后后面的40个线程进入阻塞,然后只有

释放链接之后,等待的线程就会有一个拿到,然后越后面的线程等待的时间就越长,然后一直到所有的线程执行

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

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

2.4 Exchanger

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

public Exchanger() {
	participant = new Participant();
}

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

package com.exchanger;

import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.Exchanger;

/**
 * 线程之间交换数据
 */
public class UseExchange {

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

    public static void main(String[] args) {
        new Thread() {
            @Override
            public void run() {
                Set<String> aSet = new HashSet<>();
                aSet.add("A");
                aSet.add("B");
                aSet.add("C");
                try {
                    Set<String> 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<String> bSet = new HashSet<>();
                bSet.add("1");
                bSet.add("2");
                bSet.add("3");
                try {
                    Set<String> exchange = exchanger.exchange(bSet);
                    for (String s : exchange) {
                        System.out.println("bSet" + s);
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }.start();
    }

}

执行结果:

# 程序输出
aSet1
aSet2
aSet3
bSetA
bSetB
bSetC

通过执行结果可以清晰的看到,两个线程中的数据发生了交换,这就是 Exchanger 的线程数据交换了。

以上就是 JUC 的4大常用并发工具类了。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/937298.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

设计模式之九:迭代器与组合模式

有许多方法可以把对象堆起来成为一个集合&#xff08;Collection&#xff09;&#xff0c;比如放入数组、堆栈或散列表中。若用户直接从这些数据结构中取出对象&#xff0c;则需要知道具体是存在什么数据结构中&#xff08;如栈就用peek&#xff0c;数组[]&#xff09;。迭代器…

WebGL矩阵变换

目录 变换矩阵&#xff1a;旋转 变换矩阵&#xff1a;平移 44的旋转矩阵 示例代码&#xff1a; gl.uniformMatrix4fv&#xff08;&#xff09;规范 平移&#xff1a;相同的策略 变换矩阵&#xff1a;缩放 变换矩阵&#xff1a;旋转 对于简单的变换&#xff0c;你可以使用…

【坑】Vue中带有__ob__: Observer的数组无法遍历的问题

控制台可以打印出数据但是渲染不出结构 解决办法&#xff1a; setTimeout(() > {Bus.$emit(shareRes, this.result.filter(item > item.id id)) }, 500)替换 Bus.$emit(shareRes, this.result.filter(item > item.id id))总结 解决和总结 好像和__ob__.Observe无…

电路原理分析2:应急照明灯电路

k是线圈&#xff0c;1-2&#xff08;常开&#xff09;和2-3&#xff08;常闭&#xff09;是2个触点。 1、220v交流电正常供电时&#xff0c;变压器触头位置提供12v的电压&#xff0c;这个时候&#xff0c;v2二极管是导通状态&#xff0c;所以线圈k吸合&#xff0c;这个时候1-2…

基于Java+SpringBoot+Vue前后端分离社区医院管理系统设计和实现

博主介绍&#xff1a;✌全网粉丝30W,csdn特邀作者、博客专家、CSDN新星计划导师、Java领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精彩专…

利用 IDEA IDE 的轻量编辑模式快速查看和编辑工程外的文本文件

作为程序员, 我们都知道 IDE 的很好用的, 它的文本编辑器功能也非常的强大, 用起来非常便捷. 在长年累月的使用中, 我们也变得对其非常熟悉, 以致于使用起其它简单地轻量级的文本编辑器来, 比如什么记事本, Notepad, UltraEdit 等等呀, 觉得既不方便又不熟悉. 关键是很多的操作…

探索Java的ReentrantLock:实现并发锁的强大力量

前言&#xff1a; &#x1f44f;作者简介&#xff1a;我是笑霸final&#xff0c;一名热爱技术的在校学生。 &#x1f4dd;个人主页&#xff1a;个人主页1 || 笑霸final的主页2 &#x1f4d5;系列专栏&#xff1a;java系列 &#x1f4e7;如果文章知识点有错误的地方&#xff0c;…

单例模式:保证一个类仅有一个实例

单例模式&#xff1a;保证一个类仅有一个实例 欢迎来到设计模式系列的第二篇文章&#xff01;在上一篇中&#xff0c;我们已经对设计模式有了初步的了解&#xff0c;今天让我们深入研究第一个模式——单例模式。 什么是单例模式&#xff1f; 在软件开发中&#xff0c;我们经…

pyinstaller打包openvino 2021.4.2

打包准备 1. 测试环境准备 conda create -n opinstall python3.7 -y conda activate opinstall pip install openvino2021.4.2 pip install pyinstaller PyCharm新建openvino_install&#xff0c;选择虚拟环境opinstall&#xff0c;编写测试代码 app.py import numpy as n…

mysql基础下

文章目录 1.创建表和管理表1. 一条数据存储的过程2.MySQL中的数据类型3.创建表4.修改表5.重命名表6.删除表7.清空表 2.数据处理1. 插入数据2.更新数据3. 删除数据4. 将查询结果插入到表中 3. 约束1.约束概述2.非空约束3.唯一性约束4. 主键&#xff08;PRIMARY KEY&#xff09;约…

山西电力市场日前价格预测【2023-08-28】

日前价格预测 预测明日&#xff08;2023-08-28&#xff09;山西电力市场全天平均日前电价为319.70元/MWh。其中&#xff0c;最高日前电价为371.80元/MWh&#xff0c;预计出现在19: 15。最低日前电价为278.59元/MWh&#xff0c;预计出现在13: 00。 价差方向预测 1&#xff1a; …

秋招打卡016(0827)

文章目录 前言一、今天学习了什么&#xff1f;二、关于问题的答案1.牛客网面经2.美团后端一面3.动态规划 总结 前言 提示&#xff1a;这里为每天自己的学习内容心情总结&#xff1b; Learn By Doing&#xff0c;Now or Never&#xff0c;Writing is organized thinking. 先多…

基于海洋捕食者算法优化的BP神经网络(预测应用) - 附代码

基于海洋捕食者算法优化的BP神经网络&#xff08;预测应用&#xff09; - 附代码 文章目录 基于海洋捕食者算法优化的BP神经网络&#xff08;预测应用&#xff09; - 附代码1.数据介绍2.海洋捕食者优化BP神经网络2.1 BP神经网络参数设置2.2 海洋捕食者算法应用 4.测试结果&…

企业使用CRM如何统一销售流程管理?

销售流程我们可以理解为&#xff0c;销售人员从寻找潜在客户到最终达成交易的一系列步骤。很多企业通过CRM系统来进行销售流程管理&#xff0c;提高销售效率&#xff0c;实现销售目标。下面我们就来说说&#xff0c;CRM如何进行销售流程管理。 制定统一的销售流程&#xff1a;…

关于 Camera 预览和录像画质不一样的问题分析

1、问题背景 基于之前安卓平台的一个项目&#xff0c;客户有反馈过一个 Camera app 预览的效果&#xff0c;和录像效果不一致的问题。 这里的预览是指打开 Camera app 后直接出图的效果&#xff1b;录像的效果则是指打开 Camera app 开启录像功能&#xff0c;录制一段视频&…

【算法专题突破】双指针 - 快乐数(3)

目录 1. 题目解析 2. 算法原理 3. 代码编写 写在最后&#xff1a; 1. 题目解析 题目链接&#xff1a;202. 快乐数 - 力扣&#xff08;Leetcode&#xff09; 这道题的题目也很容易理解&#xff0c; 看一下题目给的示例就能很容易明白&#xff0c; 但是要注意一个点&#…

一篇带你了解npm的原理

npm 是 JavaScript世界的包管理工具,并且是 Node.js平台的默认包管理工具。通过 npm可以安装、共享、分发代码,管理项目依赖关系。 npm的原理 npm据称成为世界最大的包管理器?原因真的只是用户友好? 一、npm init 用来初始化一个简单的package.json文件。package.json文件…

9.3 功率放大电路的安全运行

在功率放大电路中&#xff0c;功放管既要流过大电流&#xff0c;又要承受高电压。例如&#xff0c;在 OCL 电路中&#xff0c;只有功放管满足式&#xff08;9.2.13&#xff09;所示极限值的要求&#xff0c;电路才能正常工作。因此&#xff0c;所谓功率放大电路的安全运行&…

微服务学习资料

文章目录 参考资料一. 微服务概述1. CAP理论2. BASE理论3. SpringBoot 与 SpringCloud对比 二. 服务注册&#xff1a;Zookeeper,Eureka,Nacos,Consul1. Nacos两种健康检查方式&#xff1f;2. nacos中负责负载均衡底层是如何实现的3. Nacos原理4. 临时实例和持久化(非临时)实例 …

【数据结构与算法篇】手撕八大排序算法之交换排序

​&#x1f47b;内容专栏&#xff1a; 《数据结构与算法篇》 &#x1f428;本文概括&#xff1a;常见交换排序包括冒泡排序与快速排序&#xff0c;本篇讲述冒泡排序与快速排序的思想及实现、复杂度分析。 &#x1f43c;本文作者&#xff1a; 花 蝶 &#x1f438;发布时间&#…