溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

JUC 常用的并發(fā)工具類

發(fā)布時(shí)間:2020-10-28 17:54:35 來(lái)源:億速云 閱讀:132 作者:Leah 欄目:開(kāi)發(fā)技術(shù)

這期內(nèi)容當(dāng)中小編將會(huì)給大家?guī)?lái)有關(guān)JUC 常用的并發(fā)工具類,文章內(nèi)容豐富且以專業(yè)的角度為大家分析和敘述,閱讀完這篇文章希望大家可以有所收獲。

常用并發(fā)工具類:

  •   CountDownLatch
  •   CyclicBarrier
  •   Semaphore
  •   ExChanger

CountDownLatch:

  CountDownLatch,俗稱閉鎖,作用是類似加強(qiáng)版的Join,是讓一組線程等待其他的線程完成工作以后才執(zhí)行

  就比如在啟動(dòng)框架服務(wù)的時(shí)候,我們主線程需要在環(huán)境線程初始化完成之后才能啟動(dòng),這時(shí)候我們就可以實(shí)現(xiàn)使用CountDownLatch來(lái)完成

/**
   * Constructs a {@code CountDownLatch} initialized with the given count.
   *
   * @param count the number of times {@link #countDown} must be invoked
   *    before threads can pass through {@link #await}
   * @throws IllegalArgumentException if {@code count} is negative
   */
  public CountDownLatch(int count) {
    if (count < 0) throw new IllegalArgumentException("count < 0");
    this.sync = new Sync(count);
  }

  在源碼中可以看到,創(chuàng)建CountDownLatch時(shí),需要傳入一個(gè)int類型的參數(shù),將決定在執(zhí)行次扣減之后,等待的線程被喚醒

JUC 常用的并發(fā)工具類

   通過(guò)這個(gè)類圖就可以知道其實(shí)CountDownLatch并沒(méi)有多少東西

  方法介紹:

  •     CountDownLatch:初始化方法
  •     await:等待方法,同時(shí)帶參數(shù)的是超時(shí)重載方法
  •     countDown:每執(zhí)行一次,計(jì)數(shù)器減一,就是初始化傳入的數(shù)字,也代表著一個(gè)線程完成了任務(wù)
  •     getCount:獲取當(dāng)前值
  •     toString:這個(gè)就不用說(shuō)了

  里面的Sync是一個(gè)內(nèi)部類,外面的方法其實(shí)都是操作這個(gè)內(nèi)部類的,這個(gè)內(nèi)部類繼承了AQS,實(shí)現(xiàn)的標(biāo)準(zhǔn)方法,AQS將在后面的章節(jié)寫

JUC 常用的并發(fā)工具類

主線程中創(chuàng)建CountDownLatch(3),然后主線程await阻塞,然后線程A,B,C各自完成了任務(wù),調(diào)用了countDown,之后,每個(gè)線程調(diào)用一次計(jì)數(shù)器就會(huì)減一,初始是3,然后A線程調(diào)用后變成2,B線程調(diào)用后變成1,C線程調(diào)用后,變成0,這時(shí)就會(huì)喚醒正在await的主線程,然后主線程繼續(xù)執(zhí)行

說(shuō)一千道一萬(wàn),不如代碼寫幾行,上代碼:

休眠工具類,之后的代碼都會(huì)用到

package org.dance.tools;

import java.util.concurrent.TimeUnit;

/**
 * 類說(shuō)明:線程休眠輔助工具類
 */
public class SleepTools {

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

  /**
   * 按毫秒數(shù)休眠
   * @param seconds 毫秒數(shù)
   */
  public static final void ms(int seconds) {
    try {
      TimeUnit.MILLISECONDS.sleep(seconds);
    } catch (InterruptedException e) {
    }
  }
}
package org.dance.day2.util;

import org.dance.tools.SleepTools;

import java.util.concurrent.CountDownLatch;

/**
 * CountDownLatch的使用,有五個(gè)線程,6個(gè)扣除點(diǎn)
 * 扣除完成后主線程和業(yè)務(wù)線程,才能執(zhí)行工作
 * 扣除點(diǎn)一般都是大于等于需要初始化的線程的
 * @author ZYGisComputer
 */
public class UseCountDownLatch {

  /**
   * 設(shè)置為6個(gè)扣除點(diǎn)
   */
  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 .....");

      // 執(zhí)行扣減 扣減不代表結(jié)束
      countDownLatch.countDown();

      for (int i = 0; i < 2; i++) {
        System.out.println("thread_" + Thread.currentThread().getId() + ".....continue do its work");
      }

    }
  }

  /**
   * 業(yè)務(wù)線程
   */
  private static class BusiThread implements Runnable {

    @Override
    public void run() {

      // 業(yè)務(wù)線程需要在等初始化完畢后才能執(zhí)行
      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) {

    // 創(chuàng)建單獨(dú)的初始化線程
    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();
    // 啟動(dòng)業(yè)務(wù)線程
    new Thread(new BusiThread()).start();
    // 啟動(dòng)初始化線程
    for (int i = 0; i <= 3; i++) {
      new Thread(new InitThread()).start();
    }
    // 主線程進(jìn)入等待
    try {
      countDownLatch.await();
      System.out.println("Main do ites work.....");
    } catch (InterruptedException e) {
      e.printStackTrace();
    }

  }

}

返回結(jié)果:

thread_13 ready init work .....
thread_13.....continue do its work
thread_13.....continue do its work
thread_14 ready init work .....
thread_14.....continue do its work
thread_14.....continue do its work
thread_15 ready init work .....
thread_15.....continue do its work
thread_11 ready init work step 1st.....
begin stop 2nd.....
thread_16 ready init work .....
thread_16.....continue do its work
thread_16.....continue do its work
thread_15.....continue do its work
thread_11 ready init work step 2nd.....
Main do ites work.....
BusiThread 12 do business-----
BusiThread 12 do business-----
BusiThread 12 do business-----

通過(guò)返回結(jié)果就可以很直接的看到業(yè)務(wù)線程是在初始化線程完全跑完之后,才開(kāi)始執(zhí)行的

CyclicBarrier:

  CyclicBarrier,俗稱柵欄鎖,作用是讓一組線程到達(dá)某個(gè)屏障,被阻塞,一直到組內(nèi)的最后一個(gè)線程到達(dá),然后屏障開(kāi)放,接著,所有的線程繼續(xù)運(yùn)行

  這個(gè)感覺(jué)和CountDownLatch有點(diǎn)相似,但是其實(shí)是不一樣的,所謂的差別,將在下面詳解

  CyclicBarrier的構(gòu)造參數(shù)有兩個(gè)

/**
   * Creates a new {@code CyclicBarrier} that will trip when the
   * given number of parties (threads) are waiting upon it, and
   * does not perform a predefined action when the barrier is tripped.
   *
   * @param parties the number of threads that must invoke {@link #await}
   *    before the barrier is tripped
   * @throws IllegalArgumentException if {@code parties} is less than 1
   */
  public CyclicBarrier(int parties) {
    this(parties, null);
  }
/**
   * Creates a new {@code CyclicBarrier} that will trip when the
   * given number of parties (threads) are waiting upon it, and which
   * will execute the given barrier action when the barrier is tripped,
   * performed by the last thread entering the barrier.
   *
   * @param parties the number of threads that must invoke {@link #await}
   *    before the barrier is tripped
   * @param barrierAction the command to execute when the barrier is
   *    tripped, or {@code null} if there is no action
   * @throws IllegalArgumentException if {@code parties} is less than 1
   */
  public CyclicBarrier(int parties, Runnable barrierAction) {
    if (parties <= 0) throw new IllegalArgumentException();
    this.parties = parties;
    this.count = parties;
    this.barrierCommand = barrierAction;
  }

很明顯能感覺(jué)出來(lái),上面的構(gòu)造參數(shù)調(diào)用了下面的構(gòu)造參數(shù),是一個(gè)構(gòu)造方法重載

首先這個(gè)第一個(gè)參數(shù)也樹(shù)Int類型的,傳入的是執(zhí)行線程的個(gè)數(shù),這個(gè)數(shù)量和CountDownLatch不一樣,這個(gè)數(shù)量是需要和線程數(shù)量吻合的,CountDownLatch則不一樣,CountDownLatch可以大于等于,而CyclicBarrier只能等于,然后是第二個(gè)參數(shù),第二個(gè)參數(shù)是barrierAction,這個(gè)參數(shù)是當(dāng)屏障開(kāi)放后,執(zhí)行的任務(wù)線程,如果當(dāng)屏障開(kāi)放后需要執(zhí)行什么任務(wù),可以寫在這個(gè)線程中

JUC 常用的并發(fā)工具類

主線程創(chuàng)建CyclicBarrier(3,barrierAction),然后由線程開(kāi)始執(zhí)行,線程A,B執(zhí)行完成后都調(diào)用了await,然后他們都在一個(gè)屏障前阻塞者,需要等待線程C也,執(zhí)行完成,調(diào)用await之后,然后三個(gè)線程都達(dá)到屏障后,屏障開(kāi)放,然后線程繼續(xù)執(zhí)行,并且barrierAction在屏障開(kāi)放的一瞬間也開(kāi)始執(zhí)行

上代碼:

package org.dance.day2.util;

import org.dance.tools.SleepTools;

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

/**
 * CyclicBarrier的使用
 *
 * @author ZYGisComputer
 */
public class UseCyclicBarrier {

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

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

  /**
   * 結(jié)果打印線程
   * 用來(lái)演示CyclicBarrier的第二個(gè)參數(shù),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() {

      // 獲取當(dāng)前線程的ID
      long id = Thread.currentThread().getId();

      // 放入統(tǒng)計(jì)容器中
      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();
    }

  }

}

返回結(jié)果:

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

通過(guò)返回結(jié)果可以看出前面的11 14 15三個(gè)線程沒(méi)有進(jìn)入if語(yǔ)句塊,在執(zhí)行到await的時(shí)候進(jìn)入了等待,而另外12 13兩個(gè)線程進(jìn)入到了if語(yǔ)句塊當(dāng)中,多休眠了1秒多,然后當(dāng)5個(gè)線程同時(shí)到達(dá)await的時(shí)候,屏障開(kāi)放,執(zhí)行了barrierAction線程,然后線程組繼續(xù)執(zhí)行

解釋一下CountDownLatch和CyclicBarrier的卻別吧!

首先就是CountDownLatch的構(gòu)造參數(shù)傳入的數(shù)量一般都是大于等于線程,數(shù)量的,因?yàn)樗怯械谌娇刂频?可以扣減多次,然后就是CyclicBarrier的構(gòu)造參數(shù)第一個(gè)參數(shù)傳入的數(shù)量一定是等于線程的個(gè)數(shù)的,因?yàn)樗怯梢唤M線程自身控制的

區(qū)別

CountDownLatchCyclicBarrier
控制第三方控制自身控制
傳入數(shù)量大于等于線程數(shù)量等于線程數(shù)量

Semaphore:

  Semaphore,俗稱信號(hào)量,作用于控制同時(shí)訪問(wèn)某個(gè)特定資源的線程數(shù)量,用在流量控制

  一說(shuō)特定資源控制,那么第一時(shí)間就想到了數(shù)據(jù)庫(kù)連接..

  之前用等待超時(shí)模式寫了一個(gè)數(shù)據(jù)庫(kù)連接池,打算用這個(gè)Semaphone也寫一個(gè)

/**
   * 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);
  }

在源碼中可以看到在構(gòu)建Semaphore信號(hào)量的時(shí)候,需要傳入許可證的數(shù)量,這個(gè)數(shù)量就是資源的最大允許的訪問(wèn)的線程數(shù)

接下里用信號(hào)量實(shí)現(xiàn)一個(gè)數(shù)據(jù)庫(kù)連接池

連接對(duì)象

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;

/**
 * 數(shù)據(jù)庫(kù)連接
 * @author ZYGisComputer
 */
public class SqlConnection implements Connection {

  /**
   * 獲取數(shù)據(jù)庫(kù)連接
   * @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<&#63;>> getTypeMap() throws SQLException {
    return null;
  }

  @Override
  public void setTypeMap(Map<String, Class<&#63;>> 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<&#63;> iface) throws SQLException {
    return false;
  }
}

連接池對(duì)象

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;

/**
 * 使用信號(hào)量控制數(shù)據(jù)庫(kù)的鏈接和釋放
 *
 * @author ZYGisComputer
 */
public class DBPoolSemaphore {

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

  /**
   * useful 代表可用連接
   * useless 代表已用連接
   * 為什么要使用兩個(gè)Semaphore呢&#63;是因?yàn)?在連接池中不只有連接本身是資源,空位也是資源,也需要記錄
   */
  private final Semaphore useful, useless;

  /**
   * 連接池
   */
  private final static LinkedList<Connection> POOL = new LinkedList<>();

  /**
   * 使用靜態(tài)塊初始化池
   */
  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);
  }

  /**
   * 獲取數(shù)據(jù)庫(kù)連接
   *
   * @return 連接對(duì)象
   */
  public Connection takeConnection() throws InterruptedException {
    // 可用許可證減一
    useful.acquire();
    Connection connection;
    synchronized (POOL) {
      connection = POOL.removeFirst();
    }
    // 不可用許可證數(shù)量加一
    useless.release();
    return connection;
  }

  /**
   * 釋放鏈接
   *
   * @param connection 連接對(duì)象
   */
  public void returnConnection(Connection connection) throws InterruptedException {
    if(null!=connection){
      // 打印日志
      System.out.println("當(dāng)前有"+useful.getQueueLength()+"個(gè)線程等待獲取連接,,"
          +"可用連接有"+useful.availablePermits()+"個(gè)");
      // 不可用許可證減一
      useless.acquire();
      synchronized (POOL){
        POOL.addLast(connection);
      }
      // 可用許可證加一
      useful.release();
    }
  }

}

測(cè)試類:

package org.dance.day2.util.pool;

import org.dance.tools.SleepTools;

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

/**
 * 測(cè)試Semaphore
 * @author ZYGisComputer
 */
public class UseSemaphore {

  /**
   * 連接池
   */
  public static final DBPoolSemaphore pool = new DBPoolSemaphore();

  private static class BusiThread extends Thread{
    @Override
    public void run() {
      // 隨機(jī)數(shù)工具類 為了讓每個(gè)線程持有連接的時(shí)間不一樣
      Random random = new Random();
      long start = System.currentTimeMillis();
      try {
        Connection connection = pool.takeConnection();
        System.out.println("Thread_"+Thread.currentThread().getId()+
            "_獲取數(shù)據(jù)庫(kù)連接耗時(shí)["+(System.currentTimeMillis()-start)+"]ms.");
        // 模擬使用連接查詢數(shù)據(jù)
        SleepTools.ms(100+random.nextInt(100));
        System.out.println("查詢數(shù)據(jù)完成歸還連接");
        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();
    }
  }

}

測(cè)試返回結(jié)果:

Thread_11_獲取數(shù)據(jù)庫(kù)連接耗時(shí)[0]ms.
Thread_12_獲取數(shù)據(jù)庫(kù)連接耗時(shí)[0]ms.
Thread_13_獲取數(shù)據(jù)庫(kù)連接耗時(shí)[0]ms.
Thread_14_獲取數(shù)據(jù)庫(kù)連接耗時(shí)[0]ms.
Thread_15_獲取數(shù)據(jù)庫(kù)連接耗時(shí)[0]ms.
Thread_16_獲取數(shù)據(jù)庫(kù)連接耗時(shí)[0]ms.
Thread_17_獲取數(shù)據(jù)庫(kù)連接耗時(shí)[0]ms.
Thread_18_獲取數(shù)據(jù)庫(kù)連接耗時(shí)[0]ms.
Thread_19_獲取數(shù)據(jù)庫(kù)連接耗時(shí)[0]ms.
Thread_20_獲取數(shù)據(jù)庫(kù)連接耗時(shí)[0]ms.
查詢數(shù)據(jù)完成歸還連接
當(dāng)前有40個(gè)線程等待獲取連接,,可用連接有0個(gè)
Thread_21_獲取數(shù)據(jù)庫(kù)連接耗時(shí)[112]ms.
查詢數(shù)據(jù)完成歸還連接
...................查詢數(shù)據(jù)完成歸還連接
當(dāng)前有2個(gè)線程等待獲取連接,,可用連接有0個(gè)
Thread_59_獲取數(shù)據(jù)庫(kù)連接耗時(shí)[637]ms.
查詢數(shù)據(jù)完成歸還連接
當(dāng)前有1個(gè)線程等待獲取連接,,可用連接有0個(gè)
Thread_60_獲取數(shù)據(jù)庫(kù)連接耗時(shí)[660]ms.
查詢數(shù)據(jù)完成歸還連接
當(dāng)前有0個(gè)線程等待獲取連接,,可用連接有0個(gè)
查詢數(shù)據(jù)完成歸還連接...................
當(dāng)前有0個(gè)線程等待獲取連接,,可用連接有8個(gè)
查詢數(shù)據(jù)完成歸還連接
當(dāng)前有0個(gè)線程等待獲取連接,,可用連接有9個(gè)

通過(guò)執(zhí)行結(jié)果可以很明確的看到,一上來(lái)就有10個(gè)線程獲取到了連接,,然后后面的40個(gè)線程進(jìn)入阻塞,然后只有釋放鏈接之后,等待的線程就會(huì)有一個(gè)拿到,然后越后面的線程等待的時(shí)間就越長(zhǎng),然后一直到所有的線程執(zhí)行完畢

最后打印的可用連接有九個(gè)不是因?yàn)樯倭艘粋€(gè)是因?yàn)樵卺尫胖按蛴〉?不是錯(cuò)誤

從結(jié)果中可以看到,我們對(duì)連接池中的資源的到了控制,這就是信號(hào)量的流量控制

Exchanger:

  Exchanger,俗稱交換器,用于在線程之間交換數(shù)據(jù),但是比較受限,因?yàn)橹荒軆蓚€(gè)線程之間交換數(shù)據(jù)

/**
   * Creates a new Exchanger.
   */
  public Exchanger() {
    participant = new Participant();
  }

這個(gè)構(gòu)造函數(shù)沒(méi)有什么好說(shuō)的,也沒(méi)有入?yún)?只有在創(chuàng)建的時(shí)候指定一下需要交換的數(shù)據(jù)的泛型即可,下面看代碼

package org.dance.day2.util;

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

/**
 * 線程之間交換數(shù)據(jù)
 * @author ZYGisComputer
 */
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();

  }

}

執(zhí)行結(jié)果:

bSetA
bSetB
bSetC
aSet1
aSet2
aSet3

通過(guò)執(zhí)行結(jié)果可以清晰的看到,兩個(gè)線程中的數(shù)據(jù)發(fā)生了交換,這就是Exchanger的線程數(shù)據(jù)交換了

上述就是小編為大家分享的JUC 常用的并發(fā)工具類了,如果剛好有類似的疑惑,不妨參照上述分析進(jìn)行理解。如果想知道更多相關(guān)知識(shí),歡迎關(guān)注億速云行業(yè)資訊頻道。

向AI問(wèn)一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI