您好,登錄后才能下訂單哦!
這期內(nèi)容當(dāng)中小編將會(huì)給大家?guī)?lái)有關(guān)JUC 常用的并發(fā)工具類,文章內(nèi)容豐富且以專業(yè)的角度為大家分析和敘述,閱讀完這篇文章希望大家可以有所收獲。
常用并發(fā)工具類:
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í)行次扣減之后,等待的線程被喚醒
通過(guò)這個(gè)類圖就可以知道其實(shí)CountDownLatch并沒(méi)有多少東西
方法介紹:
里面的Sync是一個(gè)內(nèi)部類,外面的方法其實(shí)都是操作這個(gè)內(nèi)部類的,這個(gè)內(nèi)部類繼承了AQS,實(shí)現(xiàn)的標(biāo)準(zhǔn)方法,AQS將在后面的章節(jié)寫
主線程中創(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è)線程中
主線程創(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ū)別
CountDownLatch | CyclicBarrier | |
控制 | 第三方控制 | 自身控制 |
傳入數(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<?>> 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; } }
連接池對(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呢?是因?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è)資訊頻道。
免責(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)容。