多線程基礎知識點總結(一)

爲何使用多線程

  • 單核環境下,I/O阻塞時可有效利用CPU資源
  • 多核環境下,可以有效利用各個cpu資源。

如何創建線程

  • 繼承Thread類,重寫run方法。
  • 實現Runnable接口,實現run方法,new Thread(runnable)。

如何啓動一個線程

  • 調用Thread對象的start()方法。
  • 錯誤的方式:直接調用run()方法,只是在當前線程裏同步的運行了業務邏輯而已。

使用過程中面臨的問題

線程安全

多個線程在讀寫共享變量時,就可能會發生讀取非最新,寫入被覆蓋等情況。可以不使用共享變量來規避這種問題,如果一定要使用共享變量,那麼就需要額外做一些同步控制,才能保證邏輯正確。

共享變量
  • 加鎖同步
    • sychronized關鍵字
      • 可以標記在方法上,針對方法調用進行同步。
        • 針對實例方法,鎖定的就是實例對象。
        • 針對靜態方法,鎖定的就是所在類的class對象。
      • 可以包圍一塊代碼,進行塊內代碼進行同步。
        • 需要藉助於一個對象,可以是this。也可以new Object()。
        • 不要使用String/Integer/Long等簡單類型。主要是怕(字符串字面量/數值常量池)這些共享的變量,被不同的業務邏輯(其他人也這麼用)使用時,會發生鎖放大的情況。
    • Lock類
      • 控制的粒度更細。
      • 需要顯示的調用lock()和unlock(),unlock()必須放到finally中。
      • 鎖定的就是lock和unlock之間的邏輯。
      • 可以用tryLock實現超時放棄機制,超過一定時間獲取不到鎖,則放棄。
      • 可以實現讀寫鎖,在讀多寫少的情況下,可以有效提升性能。
      • 可以在構造時傳入ture,設置爲公平模式(fifo,不是完全公平,儘可能而已)。
      • 可以通過lockInterupptly(),設置鎖可以被中斷,具體的中斷要通過持有鎖的線程調用interrupt()。
      • 底層實現用到了cas。
  • 原子類
    • 基於樂觀鎖的實現思想。
    • 大多數採用cas方式實現。
    • 基本類型對應的原子類比較常用:AtomicInteger/AtomicLong。
    • 引用類型對應的原子類AtomicReference
      • 只能保證引用地址的併發安全。
      • 不能保證內部引用類型的屬性的併發安全。
  • 同步集合
    • Collections.synchronizedXXX(xxx)
不共享變量
  • 利用不可變類、不可變屬性,變量不可變就不存在併發寫問題。
  • 利用TreadLocal模式,爲了每個線程擁有各自獨立的變量。

死鎖

表現就是兩個線程互相持有對方等待獲取的鎖,如何避免呢?

  • 同步塊儘可能的小(鎖粒度要細)。
  • 儘量不要在一個同步塊內鎖定多個對象。
  • 如果確實要鎖定多個對象,那麼確保每個線程都以相同的順序獲取共享資源。

線程調度

線程優先級
  • 高優先級的線程更有可能先獲取CPU資源(不嚴格保證)
  • 可以通過Thread對象的setPriority方法設置優先級
  • 建議設置優先級只在三個常量值(1,5,10)中選擇
線程調度器
  • 協作式:你情我願
  • 搶佔式:弱肉強食(大部分虛擬機都採用這種方式)
讓出執行權
  • 阻塞
    • I/O阻塞(寫等待、讀等待)
      • 不再佔用cpu資源
      • 不會讓出持有的鎖
    • 鎖阻塞(等待獲取鎖)
  • 放棄(謙讓)
    • 調用Thread.yield()方法。
    • 放棄(讓出)cpu的執行權。
      • 不會讓出持有的鎖
  • 睡眠
    • 調用Thread.sleep方法(可設置睡眠時間)。
    • 不會讓出持有的鎖
    • 可以被中斷(調用某個Thread對象.interrupt()方法)。
    • 避免在同步方法或同步塊內調用。(自己持有了鎖,自己不幹活還睡覺,會導致其他線程等待鎖時間過長)
  • 連接(意思是,好吧,你先來執行,你完事,我再接着執行。)
    • 調用某個Thread對象.join()方法(可設置超時時間)。
    • 放棄當前線程持有的鎖
    • join可以被中斷。
  • 等待
    • 首先要通過synchronize方式獲取某個對象的鎖。
    • 調用該對象的wait方法(可設置等待時間),既可讓當前線程進入等待狀態
    • 放棄當前線程持有的鎖
    • 可以被中斷。
    • 其他線程通過調用該對象的notify或者nodityAll方法,喚醒等待的線程。
    • 喚醒後,只是說你不要睡了,起來幹活,但是想幹活,還要重新獲取鎖,獲取到了。才能繼續執行。
  • 結束
    • 線程run方法執行完畢,線程會被銷燬,其他線程可以接管CPU,自然就等於讓出了執行權。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章