<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
大家好,我是小郭,之前分享了CountDownLatch的使用,我們知道用來控制並行流程的同步工具,主要的作用是為了等待多個執行緒同時完成任務後,在進行主執行緒任務。
萬萬沒想到,在生產環境中竟然翻車了,因為沒有考慮到一些場景,導致了CountDownLatch出現了問題,接下來來分享一下由於CountDownLatch導致的問題。
【執行緒】並行流程控制的同步工具-CountDownLatch
先簡單介紹下業務場景,針對使用者批次下載的檔案進行修改上傳
為了提高執行的速度,所以在採用執行緒池去執行 下載-修改-上傳 的操作,並在全部執行完之後統一提交儲存檔案地址到資料庫,於是加入了CountDownLatch來進行控制。
根據服務本身情況,自定義一個執行緒池
public static ExecutorService testExtcutor() { return new ThreadPoolExecutor( 2, 2, 0L, TimeUnit.SECONDS, new LinkedBlockingQueue<>(1)); }
模擬執行
public static void main(String[] args) { // 下載檔案總數 List<Integer> resultList = new ArrayList<>(100); IntStream.range(0,100).forEach(resultList::add); // 下載檔案分段 List<List<Integer>> split = CollUtil.split(resultList, 10); ExecutorService executorService = BaseThreadPoolExector.testExtcutor(); CountDownLatch countDownLatch = new CountDownLatch(100); for (List<Integer> list : split) { executorService.execute(() -> { list.forEach(i ->{ try { // 模擬業務操作 Thread.sleep(500); System.out.println("任務進入"); } catch (InterruptedException e) { e.printStackTrace(); System.out.println(e.getMessage()); } finally { System.out.println(countDownLatch.getCount()); countDownLatch.countDown(); } }); }); } try { countDownLatch.await(); System.out.println("countDownLatch.await()"); } catch (InterruptedException e) { e.printStackTrace(); } }
一開始我個人感覺沒有什麼問題,反正finally都能夠做減一的操作,到最後呼叫await方法,進行主執行緒任務
Exception in thread "main" java.util.concurrent.RejectedExecutionException: Task java.util.concurrent.FutureTask@300ffa5d rejected from java.util.concurrent.ThreadPoolExecutor@1f17ae12[Running, pool size = 2, active threads = 2, queued tasks = 1, completed tasks = 0] at java.util.concurrent.ThreadPoolExecutor$AbortPolicy.rejectedExecution(ThreadPoolExecutor.java:2063) at java.util.concurrent.ThreadPoolExecutor.reject(ThreadPoolExecutor.java:830) at java.util.concurrent.ThreadPoolExecutor.execute(ThreadPoolExecutor.java:1379) at java.util.concurrent.AbstractExecutorService.submit(AbstractExecutorService.java:112) at Thread.executor.executorTestBlock.main(executorTestBlock.java:28) 任務進入 countDownLatch.countDown 任務進入 countDownLatch.countDown 任務進入 countDownLatch.countDown 任務進入 countDownLatch.countDown 任務進入 countDownLatch.countDown 任務進入 countDownLatch.countDown 任務進入 countDownLatch.countDown 任務進入 countDownLatch.countDown 任務進入 countDownLatch.countDown 任務進入 countDownLatch.countDown 任務進入 countDownLatch.countDown 任務進入 countDownLatch.countDown 任務進入 countDownLatch.countDown 任務進入 countDownLatch.countDown 任務進入 countDownLatch.countDown
由於任務數量較多,阻塞佇列中已經塞滿了,所以預設的拒絕策略,當佇列滿時,處理策略報錯異常,
要注意這個異常是執行緒池,自己丟擲的,不是我們迴圈裡面列印出來的,
這也造成了,線上這個執行緒池被阻塞了,他永遠也呼叫不到await方法,
利用jstack,我們就能夠看到有問題
"pool-1-thread-2" #12 prio=5 os_prio=31 tid=0x00007ff6198b7000 nid=0xa903 waiting on condition [0x0000700001c64000] java.lang.Thread.State: WAITING (parking) at sun.misc.Unsafe.park(Native Method) - parking to wait for <0x000000076b2283f8> (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject) at java.util.concurrent.locks.LockSupport.park(LockSupport.java:175) at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(AbstractQueuedSynchronizer.java:2039) at java.util.concurrent.LinkedBlockingQueue.take(LinkedBlockingQueue.java:442) at java.util.concurrent.ThreadPoolExecutor.getTask(ThreadPoolExecutor.java:1074) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1134) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) at java.lang.Thread.run(Thread.java:748) "pool-1-thread-1" #11 prio=5 os_prio=31 tid=0x00007ff6198b6800 nid=0x5903 waiting on condition [0x0000700001b61000] java.lang.Thread.State: WAITING (parking) at sun.misc.Unsafe.park(Native Method) - parking to wait for <0x000000076b2283f8> (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject) at java.util.concurrent.locks.LockSupport.park(LockSupport.java:175) at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(AbstractQueuedSynchronizer.java:2039) at java.util.concurrent.LinkedBlockingQueue.take(LinkedBlockingQueue.java:442) at java.util.concurrent.ThreadPoolExecutor.getTask(ThreadPoolExecutor.java:1074) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1134) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) at java.lang.Thread.run(Thread.java:748)
調大阻塞佇列,但是問題來了,到底多少阻塞佇列才是大呢,如果太大了會不由又造成記憶體溢位等其他的問題
在第一個的基礎上,我們修改了拒絕策略,當觸發拒絕策略的時候,用呼叫者所在的執行緒來執行任務
public static ThreadPoolExecutor queueExecutor(BlockingQueue<Runnable> workQueue){ return new ThreadPoolExecutor( size, size, 0L, TimeUnit.SECONDS, workQueue, new ThreadPoolExecutor.CallerRunsPolicy()); }
你可能又會想說,會不會任務數量太多,導致呼叫者所在的執行緒執行不過來,任務提交的效能急劇下降
那我們就應該自定義拒絕策略,將這下排隊的訊息記錄下來,採用補償機制的方式去執行
同時也要注意上面的那個異常是執行緒池丟擲來的,我們自己也需要將執行緒池進行try catch,記錄問題資料,並且在finally中執行countDownLatch.countDown來避免,執行緒池的使用
目前根據業務部門的反饋,業務實際中任務數不很特別多的情況,所以暫時先採用了第二種方式去解決這個線上問題
在這裡我們也可以看到,如果沒有正確的關閉countDownLatch,可能會導致一直等待,這也是我們需要注意的。
工具雖然好,但是依然要注意他帶來的問題,沒有正確的去處理好,引發的一系列連鎖反應。
以上就是java並行使用CountDownLatch在生產環境翻車剖析的詳細內容,更多關於java並行CountDownLatch的資料請關注it145.com其它相關文章!
相關文章
<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
综合看Anker超能充系列的性价比很高,并且与不仅和iPhone12/苹果<em>Mac</em>Book很配,而且适合多设备充电需求的日常使用或差旅场景,不管是安卓还是Switch同样也能用得上它,希望这次分享能给准备购入充电器的小伙伴们有所
2021-06-01 09:31:42
除了L4WUDU与吴亦凡已经多次共事,成为了明面上的厂牌成员,吴亦凡还曾带领20XXCLUB全队参加2020年的一场音乐节,这也是20XXCLUB首次全员合照,王嗣尧Turbo、陈彦希Regi、<em>Mac</em> Ova Seas、林渝植等人全部出场。然而让
2021-06-01 09:31:34
目前应用IPFS的机构:1 谷歌<em>浏览器</em>支持IPFS分布式协议 2 万维网 (历史档案博物馆)数据库 3 火狐<em>浏览器</em>支持 IPFS分布式协议 4 EOS 等数字货币数据存储 5 美国国会图书馆,历史资料永久保存在 IPFS 6 加
2021-06-01 09:31:24
开拓者的车机是兼容苹果和<em>安卓</em>,虽然我不怎么用,但确实兼顾了我家人的很多需求:副驾的门板还配有解锁开关,有的时候老婆开车,下车的时候偶尔会忘记解锁,我在副驾驶可以自己开门:第二排设计很好,不仅配置了一个很大的
2021-06-01 09:30:48
不仅是<em>安卓</em>手机,苹果手机的降价力度也是前所未有了,iPhone12也“跳水价”了,发布价是6799元,如今已经跌至5308元,降价幅度超过1400元,最新定价确认了。iPhone12是苹果首款5G手机,同时也是全球首款5nm芯片的智能机,它
2021-06-01 09:30:45