Java並發編程:多線程如何實現阻塞與喚醒

2021-01-07 騰訊網

線程的阻塞和喚醒在多線程並發過程中是一個關鍵點,當線程數量達到很大的數量級時,並發可能帶來很多隱蔽的問題。如何正確暫停一個線程,暫停後又如何在一個要求的時間點恢復,這些都需要仔細考慮的細節。Java為我們提供了多種API來對線程進行阻塞和喚醒操作,比如suspend與resume、sleep、wait與notify以及park與unpark等等。

01

睡眠

控制線程阻塞與喚醒的最簡單方式就是sleep了,Java通過sleep(n)方法能讓線程進入到阻塞等待狀態,直到休眠時間達到指定值後自動喚醒。該方法簡單也常用,但這種方式比較死板,需要我們預先確定線程進入阻塞的時間。而有些場景實際上我們根本沒辦法確定睡眠時間,這是sleep方式的最大劣勢。

sleep的使用很簡單,下面為一個例子。讓當前線程睡眠2000ms,最終輸出為"Sleep time in ms = 2000"。

02

掛起與恢復

在Java發展史上曾經使用suspend()、resume()方法對於線程進行阻塞喚醒,它能夠在代碼中控制阻塞和喚醒的時間節點,比起sleep()方法更加靈活。比如線程啟動後在某個時間點需要讓它掛起,這可以使用suspend方法,而當要重新喚醒它時則使用resume方法。

下面代碼為例看suspend與resume組合的實現,Thread2啟動後輸出"Second thread is suspended itself",接著自己將自己掛起。在等待2000ms後由主線程resume恢復,然後Thread2繼續輸出"Second thread runs again"。

注意:suspend(),resume(),stop()這樣的方法都被標註為過期方法,因為其不會保證釋放資源,容易產生死鎖,所以不建議使用。

03

死鎖問題

suspend與resume的組合存在很多問題,比較典型的還是死鎖問題。如下代碼,主要的邏輯代碼是主線程啟動線程mt一段時間後嘗試使用suspend()讓線程掛起,最後通過resume()恢復線程。但現實並不如願,執行到suspend()時將一直卡住,你永遠等不來「can you get here?」的輸出。

為什麼會產生上面的現象呢?其實是由死鎖導致。乍一看感覺一點問題都沒有,線程的任務僅僅只是簡單地列印字符串。其實問題的根源隱藏得較深,主線程啟動了線程mt後,線程mt開始執行execute()方法,不斷列印字符串。

- END -

相關焦點

  • Java 並發編程之美-線程相關的基礎知識
    借用 Java 並發編程實踐中的話:編寫正確的程序並不容易,而編寫正常的並發程序就更難了;相比於順序執行的情況,多線程的線程安全問題是微妙而且出乎意料的
  • JAVA並發編程:線程並發工具類Callable、Future 和FutureTask的使用
    必要時可以通過 get 方法獲取執行結果,該方法會阻塞直到任務返回結果。在Future類中一共有5個方法,如下圖所示。FutureTask 類實現了RunnableFuture 接口,RunnableFuture 繼承了Runnable接口和Future 接口,而FutureTask 實現了RunnableFuture 接口。所以它既可以作為Runnable被線程執行,又可以作為Future 得到Callable 的返回值。FutureTask,可取消的異步計算。
  • 「原創」Java並發編程系列09|基礎乾貨
    本文轉載自【微信公眾號:java進階架構師,ID:java_jiagoushi】經微信公眾號授權轉載,如需轉載與原文作者聯繫本文為何適原創並發編程系列第9篇。現在,我們進入正題:介紹並發編程的基礎性概念。
  • java並發編程之深入學習Concurrent包(十三,雙端阻塞隊列)
    引言:春節疫情的關係,宅在家,有空學習一下java並發中的內容,順便發出來請大家一起瞅瞅。上一章學習了阻塞隊列,這次一起學習下雙端阻塞隊列。LinkedBlockingDeque簡介:LinkedBlockingDeque是雙向鍊表實現的雙端阻塞隊列。該阻塞隊列可以從隊列的頭和尾進行插入和刪除,且該阻塞隊列是線程安全的。
  • Java 面試寶典!並發編程 71 道題及答案全送上!
    所以創建一個線程池是個更好的的解決方案,因為可以限制線程的數量並且可以回收再利用這些線程。利用Executors框架可以非常方便的創建一個線程池。12、什麼是阻塞隊列?阻塞隊列的實現原理是什麼?如何使用阻塞隊列來實現生產者-消費者模型?阻塞隊列是一個支持兩個附加操作的隊列。在隊列為空時,獲取元素的線程會等待隊列變為非空。
  • 原創】Java並發編程系列01|開篇獲獎感言
    所以,並發編程已經成為一項必備技能。並發編程是Java語言的重要特性之一,它能使複雜的代碼變得更簡單,從而極大地簡化複雜系統的開發。並發編程可以充分發揮多處理器系統的強大計算能力,隨著處理器數量的持續增長,如何高效的並發變得越來越重要。
  • Java面試題-多線程篇十三
    線程是作業系統能夠進行運算調度的最小單位,它被包含在進程之中,是進程中的實際運作單位。程式設計師可以通過它進行多處理器編程,你可以使用多線程對運算密集型任務提速。比如,如果一個線程完成一個任務要100毫秒,那麼用十個線程完成改任務只需10毫秒。122,線程和進程有什麼區別?
  • 【堪稱經典】JAVA多線程和並發基礎面試問答
    進程和線程之間有什麼不同?一個進程是一個獨立(self contained)的運行環境,它可以被看作一個程序或者一個應用。而線程是在進程中執行的一個任務。Java運行環境是一個包含了不同的類和程序的單一進程。線程可以被稱為輕量級進程。線程需要較少的資源來創建和駐留在進程中,並且可以共享進程中的資源。2. 多線程編程的好處是什麼?
  • 「原創」Java並發編程系列22|倒計時器CountDownLatch
    本文轉載自【微信公眾號:java進階架構師,ID:java_jiagoushi】經微信公眾號授權轉載,如需轉載與原文作者聯繫並發編程中常遇到這種情況,一個線程需要等待另外多個線程執行後再執行。遇到這種情況你一般怎麼做呢?今天就介紹一種JDk提供的解決方案來優雅的解決這一問題,那就是倒計時器CountDownLatch。
  • 徹底理解Java並發編程原理!
    Java並發編程為四大部分:計算機並發基礎知識、JDK內置並發框架、JDK並發包剖析以及其它並發知識。具體包括線程的狀態、Java線程調度策略、線程優先級、並發模型、悲觀鎖樂觀鎖、JDK各種同步器、JDK內置AQS同步器、線程與IO、Java線程與JVM線程、阻塞與喚醒機制、JDK並發包各種工具剖析、自旋、JDK內置並發鎖、CAS、synchronized、線程池、線程之間的協作等並發方面知識及原理進行深入淺出的講解。
  • Java 線程面試題 Top 50
    在典型的Java面試中, 面試官會從線程的基本概念問起, 如:為什麼你需要使用線程, 如何創建線程,用什麼方式創建線程比較好(比如:繼承thread類還是調用Runnable接口),然後逐漸問到並發問題像在Java並發編程的過程中遇到了什麼挑戰,Java內存模型,JDK1.5引入了哪些更高階的並發工具,並發編程常用的設計模式,經典多線程問題如生產者消費者,哲學家就餐,讀寫器或者簡單的有界緩衝區問題
  • Java 並發編程:Synchronized 及其實現原理
    如果其他線程已經佔用了monitor,則該線程進入阻塞狀態,直到monitor的進入數為0,再重新嘗試獲取monitor的所有權。通過這兩段描述,我們應該能很清楚的看出Synchronized的實現原理,Synchronized的語義底層是通過一個monitor的對象來完成,其實wait/notify等方法也依賴於monitor對象,這就是為什麼只有在同步的塊或者方法中才能調用wait/notify等方法,否則會拋出java.lang.IllegalMonitorStateException的異常的原因。
  • Java並發包下Java多線程並發之讀寫鎖鎖學習第五篇-讀寫鎖
    Java多線程並發之讀寫鎖本文主要內容:讀寫鎖的理論;通過生活中例子來理解讀寫鎖;讀寫鎖的代碼演示;讀寫鎖總結。通過理論(總結)-例子-代碼-然後再次總結,這四個步驟來讓大家對讀寫鎖的深刻理解。本篇是《凱哥(凱哥Java:kagejava)並發編程學習》系列之《Lock系列》教程的第七篇:《Java並發包下鎖學習第七篇:讀寫鎖》。一:讀寫鎖的理論什麼是讀寫鎖?
  • Java多線程synchronized
    本篇主要介紹Java多線程中的同步,也就是如何在Java語言中寫出線程安全的程序,如何在Java語言中解決非線程安全的相關問題。
  • 40個Java多線程問題總結
    (2)防止阻塞從程序運行效率的角度來看,單核CPU不但不會發揮出多線程的優勢,反而會因為在單核CPU上運行多線程導致線程上下文的切換,而降低程序整體的效率。但是單核CPU我們還是要應用多線程,就是為了防止阻塞。
  • Java並發編程系列23|循環屏障CyclicBarrier
    本文轉載自【微信公眾號:java進階架構師,ID:java_jiagoushi】經微信公眾號授權轉載,如需轉載與原文作者聯繫本篇介紹第二個並發工具類CyclicBarrier,CyclicBarrier的字面意思是可循環使用(Cyclic)的屏障(Barrier),分以下部分介紹:
  • 我和面試官的博弈:Java 並發編程篇
    還請大家置頂(標星)本公眾號:Java後端,第一時間接收優質博面試中問的頻率很高的一個是分布式,一個就是並發。而JUC(java.util.concurrent)裡的東西是並發編程的基石。上次的面試已經過去一段時間,在一邊努力工作的同時,我也一邊抽出時間準備 Java 並發編程的部分。今天懷著輕鬆愉快的心情,再次踏上我的大廠面試之旅。
  • Java多線程並發編程中並發容器第二篇之List的並發類講解
    Java多線程並發編程中並發容器第二篇之List的並發類講解概述本文我們將詳細講解list對應的並發容器以及用代碼來測試ArrayList、vector以及CopyOnWriteArrayList在100個線程向list中添加1000個數據後的比較
  • 「原創」Java並發編程系列14|AQS源碼分析
    本文轉載自【微信公眾號:java進階架構師,ID:java_jiagoushi】經微信公眾號授權轉載,如需轉載與原文作者聯繫本文為何適原創並發編程系列第 14 篇,文末有本系列文章匯總。AbstractQueuedSynchronizer是Java並發包java.util.concurrent的核心基礎組件,是實現Lock的基礎。
  • Java並發編程:synchronized關鍵字的實現原理——鎖膨脹過程
    ,即JVM會根據各種情況動態改變自旋次數:如果平均負載小於CPU則一直自旋如果有超過(CPU/2)個線程正在自旋,則後來線程直接阻塞如果正在自旋的線程發現Owner發生了變化則延遲自旋時間(自旋計數)或進入阻塞