05.java多線程問題

目錄介紹

  • 5.0.0.1 線程池具備什麼優勢和缺點?爲何說開啓大量的線程,會下降程序的性能,那麼該如何作才能下降性能?
  • 5.0.0.3 線程中start和run方法有什麼區別?wait和sleep方法的不一樣?sleep() 、join()、yield()有什麼區別?
  • 5.0.0.4 用Java手寫一個會致使死鎖的程序,遇到這種問題解決方案是什麼?那些場景用到了死鎖機制?
  • 5.0.0.5 ThreadLocal(線程變量副本)這個類的做用是什麼?
  • 5.0.0.6 什麼是線程安全?線程安全有那幾個級別?保障線程安全有哪些手段?ReentrantLock和synchronized的區別?
  • 5.0.0.7 Volatile和Synchronized各自用途是什麼?有哪些不一樣點?Synchronize在編譯時如何實現鎖機制?
  • 5.0.0.8 wait()和sleep()的區別?各自有哪些使用場景?怎麼喚醒一個阻塞的線程?Thread.sleep(0)的做用是啥?
  • 5.0.0.9 同步和非同步、阻塞和非阻塞的概念?分別有哪些使用場景?
  • 5.0.1.0 線程的有哪些狀態?請繪製該狀態的流程圖?講一下線程的執行生命週期流程?線程若是出現了運行時異常會怎麼樣?
  • 5.0.1.1 synchronized鎖什麼?synchronized同步代碼塊還有同步方法本質上鎖住的是誰?爲何?
  • 5.0.1.2 Volatile實現原理?一個int變量,用volatile修飾,多線程去操做++,線程安全嗎?那如何才能保證i++線程安全?
  • 5.0.1.3 CAS原理是什麼?CAS實現原子操做會出現什麼問題?
  • 5.0.1.4 假若有n個網絡線程,須要當n個網絡線程完成以後,再去作數據處理,你會怎麼解決?
  • 5.0.1.5 Runnable接口和Callable接口的區別?
  • 5.0.1.6 若是提交任務時,線程池隊列已滿,這時會發生什麼?線程調度算法是什麼?
  • 5.0.1.7 什麼是樂觀鎖和悲觀鎖?
  • 5.0.1.8 線程類的構造方法、靜態塊是被哪一個線程調用的?同步方法和同步塊,哪一個是更好的選擇?同步的範圍越少越好嗎?
  • 5.0.1.9 synchonized(this)和synchonized(object)區別?Synchronize做用於方法和靜態方法區別?

好消息

  • 博客筆記大彙總【15年10月到至今】,包括Java基礎及深刻知識點,Android技術博客,Python學習筆記等等,還包括平時開發中遇到的bug彙總,固然也在工做之餘收集了大量的面試題,長期更新維護而且修正,持續完善……開源的文件是markdown格式的!同時也開源了生活博客,從12年起,積累共計500篇[近100萬字],將會陸續發表到網上,轉載請註明出處,謝謝!
  • 連接地址:https://github.com/yangchong211/YCBlogs
  • 若是以爲好,能夠star一下,謝謝!固然也歡迎提出建議,萬事起於忽微,量變引發質變!全部博客將陸續開源到GitHub!

5.0.0.1 線程池具備什麼優勢和缺點?爲何說開啓大量的線程,會下降程序的性能,那麼該如何作才能下降性能?

  • 線程池好處:
    • 1)下降資源消耗;
    • 2)提升相應速度;
    • 3)提升線程的可管理性。技術博客大總結
  • 線程池的實現原理:
    • 當提交一個新任務到線程池時,判斷核心線程池裏的線程是否都在執行。若是不是,則建立一個新的線程執行任務。若是核心線程池的線程都在執行任務,則進入下個流程。
    • 判斷工做隊列是否已滿。若是未滿,則將新提交的任務存儲在這個工做隊列裏。若是工做隊列滿了,則進入下個流程。
    • 判斷線程池是否都處於工做狀態。若是沒有,則建立一個新的工做線程來執行任務。若是滿了,則交給飽和策略來處理這個任務。

5.0.0.3 線程中start和run方法有什麼區別?wait和sleep方法的不一樣?sleep() 、join()、yield()有什麼區別?

  • 線程中start和run方法有什麼區別
    • 爲何咱們調用start()方法時會執行run()方法,爲何咱們不能直接調用run()方法?這是一個很是經典的java多線程面試問題。當你調用start()方法時你將建立新的線程,而且執行在run()方法裏的代碼。可是若是你直接調用run()方法,它不會建立新的線程也不會執行調用線程的代碼。
  • wait和sleep方法的不一樣
    • 最大的不一樣是在等待時wait會釋放鎖,而sleep一直持有鎖。Wait一般被用於線程間交互,sleep一般被用於暫停執行。
  • 一、sleep()方法
    • 在指定的毫秒數內讓當前正在執行的線程休眠(暫停執行),此操做受到系統計時器和調度程序精度和準確性的影響。 讓其餘線程有機會繼續執行,但它並不釋放對象鎖。也就是若是有Synchronized同步塊,其餘線程仍然不能訪問共享數據。注意該方法要捕獲異常
    • 好比有兩個線程同時執行(沒有Synchronized),一個線程優先級爲MAX_PRIORITY,另外一個爲MIN_PRIORITY,若是沒有Sleep()方法,只有高優先級的線程執行完成後,低優先級的線程才能執行;但當高優先級的線程sleep(5000)後,低優先級就有機會執行了。
    • 總之,sleep()可使低優先級的線程獲得執行的機會,固然也可讓同優先級、高優先級的線程有執行的機會。
  • 二、yield()方法技術博客大總結
    • yield()方法和sleep()方法相似,也不會釋放「鎖標誌」,區別在於,它沒有參數,即yield()方法只是使當前線程從新回到可執行狀態,因此執行yield()的線程有可能在進入到可執行狀態後立刻又被執行,另外yield()方法只能使同優先級或者高優先級的線程獲得執行機會,這也和sleep()方法不一樣。
  • 三、join()方法
    • Thread的非靜態方法join()讓一個線程B「加入」到另一個線程A的尾部。在A執行完畢以前,B不能工做。
    • Thread t = new MyThread(); t.start(); t.join();保證當前線程中止執行,直到該線程所加入的線程完成爲止。然而,若是它加入的線程沒有存活,則當前線程不須要中止。
  • Thread的join()有什麼做用?
    • Thread的join()的含義是等待該線程終止,即將掛起調用線程的執行,直到被調用的對象完成它的執行。好比存在兩個線程t1和t2,下述代碼表示先啓動t1,直到t1的任務結束,才輪到t2啓動。
      t1.start();
      t1.join(); 
      t2.start();

5.0.0.4 用Java手寫一個會致使死鎖的程序,遇到這種問題解決方案是什麼?那些場景用到了死鎖機制?

  • 死鎖是怎麼一回事
    • 線程A和線程B相互等待對方持有的鎖致使程序無限死循環下去。
  • 深刻理解死鎖的原理
    • 兩個線程裏面分別持有兩個Object對象:lock1和lock2。這兩個lock做爲同步代碼塊的鎖;
    • 線程1的run()方法中同步代碼塊先獲取lock1的對象鎖,Thread.sleep(xxx),時間不須要太多,50毫秒差很少了,而後接着獲取lock2的對象鎖。這麼作主要是爲了防止線程1啓動一會兒就連續得到了lock1和lock2兩個對象的對象鎖
    • 線程2的run)(方法中同步代碼塊先獲取lock2的對象鎖,接着獲取lock1的對象鎖,固然這時lock1的對象鎖已經被線程1鎖持有,線程2確定是要等待線程1釋放lock1的對象鎖的

5.0.0.5 ThreadLocal(線程變量副本)這個類的做用是什麼?

  • ThreadLocal即線程變量
    • ThreadLocal爲每一個線程維護一個本地變量。
    • 採用空間換時間,它用於線程間的數據隔離,它爲每一個使用該變量的線程提供獨立的變量副本,因此每個線程均可以獨立地改變本身的副本,而不會影響其它線程所對應的副本。從線程的角度看,目標變量就象是線程的本地變量,這也是類名中「Local」所要表達的意思。ThreadLocal的實現是以ThreadLocal對象爲鍵。任意對象爲值得存儲結構。這個結構被附帶在線程上,也就是說一個線程能夠根據一個ThreadLocal對象查詢到綁定在這個線程上的一個值。
  • ThreadLocal類是一個Map
    • ThreadLocal類中維護一個Map,用於存儲每個線程的變量副本,Map中元素的鍵爲線程對象,而值爲對應線程的變量副本。
    • ThreadLocal在Spring中發揮着巨大的做用,在管理Request做用域中的Bean、事務管理、任務調度、AOP等模塊都出現了它的身影。
    • Spring中絕大部分Bean均可以聲明成Singleton做用域,採用ThreadLocal進行封裝,所以有狀態的Bean就可以以singleton的方式在多線程中正常工做了。
  • 更多詳細參考博客:深刻研究java.lang.ThreadLocal類

5.0.0.6 什麼是線程安全?線程安全有那幾個級別?保障線程安全有哪些手段?ReentrantLock和synchronized的區別?

  • 什麼是線程安全
    • 線程安全就是當多個線程訪問一個對象時,若是不用考慮這些線程在運行時環境下的調度和交替執行,也不須要進行額外的同步,或者在調用方進行任何其餘的協調操做,調用這個對象的行爲均可以得到正確的結果,那這個對象是線程安全的。
  • 線程安全也是有幾個級別
    • 技術博客大總結
    • 不可變:
      • 像String、Integer、Long這些,都是final類型的類,任何一個線程都改變不了它們的值,要改變除非新建立一個,所以這些不可變對象不須要任何同步手段就能夠直接在多線程環境下使用
    • 絕對線程安全
      • 無論運行時環境如何,調用者都不須要額外的同步措施。要作到這一點一般須要付出許多額外的代價,Java中標註本身是線程安全的類,實際上絕大多數都不是線程安全的,不過絕對線程安全的類,Java中也有,比方說CopyOnWriteArrayList、CopyOnWriteArraySet
    • 相對線程安全
      • 相對線程安全也就是咱們一般意義上所說的線程安全,像Vector這種,add、remove方法都是原子操做,不會被打斷,但也僅限於此,若是有個線程在遍歷某個Vector、有個線程同時在add這個Vector,99%的狀況下都會出現ConcurrentModificationException,也就是fail-fast機制。
    • 線程非安全技術博客大總結
      • ArrayList、LinkedList、HashMap等都是線程非安全的類.
  • 保障線程安全有哪些手段。保證線程安全可從多線程三特性出發:
    • 原子性(Atomicity):單個或多個操做是要麼所有執行,要麼都不執行
      • Lock:保證同時只有一個線程能拿到鎖,並執行申請鎖和釋放鎖的代碼
      • synchronized:對線程加獨佔鎖,被它修飾的類/方法/變量只容許一個線程訪問
    • 可見性(Visibility):當一個線程修改了共享變量的值,其餘線程可以當即得知這個修改
      • volatile:保證新值能當即同步到主內存,且每次使用前當即從主內存刷新;
      • synchronized:在釋放鎖以前會將工做內存新值更新到主存中
    • 有序性(Ordering):程序代碼按照指令順序執行
      • volatile: 自己就包含了禁止指令重排序的語義
      • synchronized:保證一個變量在同一個時刻只容許一條線程對其進行lock操做,使得持有同一個鎖的兩個同步塊只能串行地進入
  • ReentrantLock和synchronized的區別
    • ReentrantLock與synchronized的不一樣在於ReentrantLock:
      • 等待可中斷:當持有鎖的線程長期不釋放鎖的時候,正在等待的線程能夠選擇放棄等待,改成處理其餘事情。
      • 公平鎖:多個線程在等待同一個鎖時,必須按照申請鎖的時間順序來依次得到鎖。而synchronized是非公平的,即在鎖被釋放時,任何一個等待鎖的線程都有機會得到鎖。ReentrantLock默認狀況下也是非公平的,但能夠經過帶布爾值的構造函數改用公平鎖。
      • 鎖綁定多個條件:一個ReentrantLock對象能夠經過屢次調用newCondition()同時綁定多個Condition對象。而在synchronized中,鎖對象wait()和notify()或notifyAl()只能實現一個隱含的條件,若要和多於一個的條件關聯不得不額外地添加一個鎖。
    • Synchronized是悲觀鎖機制,獨佔鎖。而Locks.ReentrantLock是,每次不加鎖而是假設沒有衝突而去完成某項操做,若是由於衝突失敗就重試,直到成功爲止。
      • ReentrantLock適用場景
      • 某個線程在等待一個鎖的控制權的這段時間須要中斷
      • 須要分開處理一些wait-notify,ReentrantLock裏面的Condition應用,可以控制notify哪一個線程,鎖能夠綁定多個條件。
      • 具備公平鎖功能,每一個到來的線程都將排隊等候。
    • 更多詳細參考博客:Lock與synchronized 的區別

5.0.0.7 Volatile和Synchronized各自用途是什麼?有哪些不一樣點?Synchronize在編譯時如何實現鎖機制?

  • Volatile和Synchronized各自用途是什麼?有哪些不一樣點?
    • 1 粒度不一樣,前者針對變量 ,後者鎖對象和類
    • 2 syn阻塞,volatile線程不阻塞
    • 3 syn保證三大特性,volatile不保證原子性
    • 4 syn編譯器優化,volatile不優化 volatile具有兩種特性:
      • 1.保證此變量對全部線程的可見性,指一條線程修改了這個變量的值,新值對於其餘線程來講是可見的,但並非多線程安全的。
      • 2.禁止指令重排序優化。
    • Volatile如何保證內存可見性:
      • 1.當寫一個volatile變量時,JMM會把該線程對應的本地內存中的共享變量刷新到主內存。
      • 2.當讀一個volatile變量時,JMM會把該線程對應的本地內存置爲無效。線程接下來將從主內存中讀取共享變量。
    • 同步:就是一個任務的完成須要依賴另一個任務,只有等待被依賴的任務完成後,依賴任務才能完成。
    • 異步:不須要等待被依賴的任務完成,只是通知被依賴的任務要完成什麼工做,只要本身任務完成了就算完成了,被依賴的任務是否完成會通知回來。(異步的特色就是通知)。 打電話和發短信來比喻同步和異步操做。
    • 阻塞:CPU停下來等一個慢的操做完成之後,纔會接着完成其餘的工做。
    • 非阻塞:非阻塞就是在這個慢的執行時,CPU去作其餘工做,等這個慢的完成後,CPU纔會接着完成後續的操做。
    • 非阻塞會形成線程切換增長,增長CPU的使用時間能不能補償系統的切換成本須要考慮。
  • Synchronize在編譯時如何實現鎖機制?
    • Synchronized進過編譯,會在同步塊的先後分別造成monitorenter和monitorexit這個兩個字節碼指令。在執行monitorenter指令時,首先要嘗試獲取對象鎖。若是這個對象沒被鎖定,或者當前線程已經擁有了那個對象鎖,把鎖的計算器加1,相應的,在執行monitorexit指令時會將鎖計算器就減1,當計算器爲0時,鎖就被釋放了。若是獲取對象鎖失敗,那當前線程就要阻塞,直到對象鎖被另外一個線程釋放爲止。

5.0.0.8 wait()和sleep()的區別?各自有哪些使用場景?怎麼喚醒一個阻塞的線程?Thread.sleep(0)的做用是啥?

  • sleep來自Thread類,和wait來自Object類
    • 調用sleep()方法的過程當中,線程不會釋放對象鎖。而調用wait方法線程會釋放對象鎖
    • sleep睡眠後不出讓系統資源,wait讓出系統資源其餘線程能夠佔用CPU
    • sleep(milliseconds)須要指定一個睡眠時間,時間一到會自動喚醒
  • 通俗解釋
    • Java程序中wait 和 sleep都會形成某種形式的暫停,它們能夠知足不一樣的須要。wait()方法用於線程間通訊,若是等待條件爲真且其它線程被喚醒時它會釋放鎖,而 sleep()方法僅僅釋放CPU資源或者讓當前線程中止執行一段時間,但不會釋放鎖。
  • 怎麼喚醒一個阻塞的線程?
    • 若是線程是由於調用了wait()、sleep()或者join()方法而致使的阻塞,能夠中斷線程,而且經過拋出InterruptedException來喚醒它;若是線程遇到了IO阻塞,無能爲力,由於IO是操做系統實現的,Java代碼並無辦法直接接觸到操做系統。
    • 技術博客大總結
  • Thread.sleep(0)的做用是啥?
    • 因爲Java採用搶佔式的線程調度算法,所以可能會出現某條線程經常獲取到CPU控制權的狀況,爲了讓某些優先級比較低的線程也能獲取到CPU控制權,可使用Thread.sleep(0)手動觸發一次操做系統分配時間片的操做,這也是平衡CPU控制權的一種操做。

5.0.0.9 同步和非同步、阻塞和非阻塞的概念?分別有哪些使用場景?

  • 同步和非同步
    • 同步和異步體現的是消息的通知機制:所謂同步,方法A調用方法B後必須等到方法B返回結果才能繼續後面的操做;所謂異步,方法A調用方法B後可以讓方法B在調用結束後經過回調等方式通知方法A
  • 阻塞和非阻塞
    • 阻塞和非阻塞側重於等待消息時的狀態:所謂阻塞,就是在結果返回以前讓當前線程掛起;所謂非阻塞,就是在等待時可作其餘事情,經過輪詢去詢問是否已返回結果

5.0.1.0 線程的有哪些狀態?請繪製該狀態的流程圖?講一下線程的執行生命週期流程?線程若是出現了運行時異常會怎麼樣?

  • 在任意一個時間點,一個線程只能有且只有其中的一種狀態
    • 新建(New):線程建立後還沒有啓動
    • 技術博客大總結
    • 運行(Runable):包括正在執行(Running)和等待着CPU爲它分配執行時間(Ready)兩種
      無限期等待(Waiting):該線程不會被分配CPU執行時間,要等待被其餘線程顯式地喚醒。如下方法會讓線程陷入無限期等待狀態:
      沒有設置Timeout參數的Object.wait()
      沒有設置Timeout參數的Thread.join()
      LockSupport.park()
    • 限期等待(Timed Waiting):該線程不會被分配CPU執行時間,但在必定時間後會被系統自動喚醒。如下方法會讓線程進入限期等待狀態:
      Thread.sleep()
      設置了Timeout參數的Object.wai()
      設置了Timeout參數的Thread.join()
      LockSupport.parkNanos()
      LockSupport.parkUntil()
    • 阻塞(Blocked):線程被阻塞。和等待狀態不一樣的是,阻塞狀態表示在等待獲取到一個排他鎖,在另一個線程放棄這個鎖的時候發生;而等待狀態表示在等待一段時間或者喚醒動做的發生,在程序等待進入同步區域的時候發生。
    • 結束(Terminated):線程已經結束執行
  • 繪製該狀態的流程圖
  • 線程若是出現了運行時異常會怎麼樣?
    • 若是這個異常沒有被捕獲的話,這個線程就中止執行了。另外重要的一點是:若是這個線程持有某個某個對象的監視器,那麼這個對象監視器會被當即釋放

5.0.1.1 synchronized鎖什麼?synchronized同步代碼塊還有同步方法本質上鎖住的是誰?爲何?

  • synchronized鎖什麼
    • 對於普通同步方法,鎖是當前實例對象;
    • 對於靜態同步方法,鎖是當前類的Class對象;
    • 對於同步方法塊,鎖是括號中配置的對象;
    • 當一個線程試圖訪問同步代碼塊時,它首先必須獲得鎖,退出或拋出異常時必須釋放鎖。synchronized用的鎖是存在Java對象頭裏的MarkWord,一般是32bit或者64bit,其中最後2bit表示鎖標誌位。
  • 本質上鎖住的是對象。
    • 在java虛擬機中,每一個對象和類在邏輯上都和一個監視器相關聯,synchronized本質上是對一個對象監視器的獲取。當執行同步代碼塊或同步方法時,執行方法的線程必須先得到該對象的監視器,才能進入同步代碼塊或同步方法;而沒有獲取到的線程將會進入阻塞隊列,直到成功獲取對象監視器的線程執行結束並釋放鎖後,纔會喚醒阻塞隊列的線程,使其從新嘗試對對象監視器的獲取。

5.0.1.2 Volatile實現原理?一個int變量,用volatile修飾,多線程去操做++,線程安全嗎?那如何才能保證i++線程安全?

  • volatile的做用和原理
    • Java代碼在編譯後會變成Java字節碼,字節碼被類加載器加載到JVM裏,JVM執行字節碼,最終須要轉化爲彙編指令在CPU上執行。
    • volatile是輕量級的synchronized(volatile不會引發線程上下文的切換和調度),它在多處理器開發中保證了共享變量的「可見性」。可見性的意思是當一個線程修改一個共享變量時,另一個線程能讀到這個修改的值。
    • 因爲內存訪問速度遠不及CPU處理速度,爲了提升處理速度,處理器不直接和內存進行通訊,而是先將系統內存的數據讀到內部緩存後在進行操做,但操做完不知道什麼時候會寫到內存。普通共享變量被修改以後,何時被寫入主存是不肯定的,當其餘線程去讀取時,此時內存中可能仍是原來的舊值,所以沒法保證可見性。若是對聲明瞭volatile的變量進行寫操做,JVM就會想處理器發送一條Lock前綴的指令,表示將當前處理器緩存行的數據寫回到系統內存。
  • 一個int變量,用volatile修飾,多線程去操做++,線程安全嗎
    • 技術博客大總結
    • 不安全
    • 案例代碼,至於打印結果就不展現呢
    • volatile只能保證可見性,並不能保證原子性。
    • i++實際上會被分紅多步完成:
      • 1)獲取i的值;
      • 2)執行i+1;
      • 3)將結果賦值給i。
    • volatile只能保證這3步不被重排序,多線程狀況下,可能兩個線程同時獲取i,執行i+1,而後都賦值結果2,實際上應該進行兩次+1操做。
      private volatile int a = 0;
      for (int x=0 ; x<=100 ; x++){
      new Thread(new Runnable() {
          @Override
          public void run() {
              a++;
              Log.e("小楊逗比Thread-------------",""+a);
          }
      }).start();
      }
  • 如何才能保證i++線程安全
    • 可使用java.util.concurrent.atomic包下的原子類,如AtomicInteger。其實現原理是採用CAS自旋操做更新值。
      for (int x=0 ; x<=100 ; x++){
      new Thread(new Runnable() {
          @Override
          public void run() {
              AtomicInteger atomicInteger = new AtomicInteger(a++);
              int i = atomicInteger.get();
              Log.e("小楊逗比Thread-------------",""+i);
          }
      }).start();
      }

5.0.1.3 CAS原理是什麼?CAS實現原子操做會出現什麼問題?

  • CAS原理是什麼
    • CAS即compare and swap的縮寫,中文翻譯成比較並交換。CAS有3個操做數,內存值V,舊的預期值A,要修改的新值B。當且僅當預期值A和內存值V相同時,將內存值V修改成B,不然什麼都不作。自旋就是不斷嘗試CAS操做直到成功爲止。
  • CAS實現原子操做會出現什麼問題
    • ABA問題。由於CAS須要在操做之的時候,檢查值有沒有發生變化,若是沒有發生變化則更新,可是若是一個值原來是A,變成,有變成A,那麼使用CAS進行檢查時會發現它的值沒有發生變化,但實際上發生了變化。ABA問題能夠經過添加版本號來解決。Java 1.5開始,JDK的Atomic包裏提供了一個類AtomicStampedReference來解決ABA問題。
    • 循環時間長開銷大。pause指令優化。
    • 只能保證一個共享變量的原子操做。能夠合併成一個對象進行CAS操做。

5.0.1.4 假若有n個網絡線程,須要當n個網絡線程完成以後,再去作數據處理,你會怎麼解決?

  • 多線程同步的問題。這種狀況能夠可使用thread.join();join方法會阻塞直到thread線程終止才返回。更復雜一點的狀況也可使用CountDownLatch,CountDownLatch的構造接收一個int參數做爲計數器,每次調用countDown方法計數器減一。作數據處理的線程調用await方法阻塞直到計數器爲0時。

5.0.1.5 Runnable接口和Callable接口的區別?

  • Runnable接口和Callable接口的區別
    • Runnable接口中的run()方法的返回值是void,它作的事情只是純粹地去執行run()方法中的代碼而已;Callable接口中的call()方法是有返回值的,是一個泛型,和Future、FutureTask配合能夠用來獲取異步執行的結果。
    • 這實際上是頗有用的一個特性,由於多線程相比單線程更難、更復雜的一個重要緣由就是由於多線程充滿着未知性,某條線程是否執行了?某條線程執行了多久?某條線程執行的時候咱們指望的數據是否已經賦值完畢?沒法得知,咱們能作的只是等待這條多線程的任務執行完畢而已。而Callable+Future/FutureTask卻能夠獲取多線程運行的結果,能夠在等待時間太長沒獲取到須要的數據的狀況下取消該線程的任務,真的是很是有用。

5.0.1.6 若是提交任務時,線程池隊列已滿,這時會發生什麼?線程調度算法是什麼?

  • 若是提交任務時,線程池隊列已滿,這時會發生什麼?
    • 若是使用的是×××隊列LinkedBlockingQueue,也就是×××隊列的話,不要緊,繼續添加任務到阻塞隊列中等待執行,由於LinkedBlockingQueue能夠近乎認爲是一個無窮大的隊列,能夠無限存聽任務
    • 技術博客大總結
    • 若是使用的是有界隊列好比ArrayBlockingQueue,任務首先會被添加到ArrayBlockingQueue中,ArrayBlockingQueue滿了,會根據maximumPoolSize的值增長線程數量,若是增長了線程數量仍是處理不過來,ArrayBlockingQueue繼續滿,那麼則會使用拒絕策略RejectedExecutionHandler處理滿了的任務,默認是AbortPolicy
  • 線程調度算法是什麼?
    • 搶佔式。一個線程用完CPU以後,操做系統會根據線程優先級、線程飢餓狀況等數據算出一個總的優先級並分配下一個時間片給某個線程執行。

5.0.1.7 什麼是樂觀鎖和悲觀鎖?

  • 什麼是樂觀鎖和悲觀鎖?
    • 樂觀鎖:就像它的名字同樣,對於併發間操做產生的線程安全問題持樂觀狀態,樂觀鎖認爲競爭不老是會發生,所以它不須要持有鎖,將比較-替換這兩個動做做爲一個原子操做嘗試去修改內存中的變量,若是失敗則表示發生衝突,那麼就應該有相應的重試邏輯。
    • 悲觀鎖:仍是像它的名字同樣,對於併發間操做產生的線程安全問題持悲觀狀態,悲觀鎖認爲競爭老是會發生,所以每次對某資源進行操做時,都會持有一個獨佔的鎖,就像synchronized,直接上了鎖就操做資源。

5.0.1.8 線程類的構造方法、靜態塊是被哪一個線程調用的?同步方法和同步塊,哪一個是更好的選擇?同步的範圍越少越好嗎?

  • 線程類的構造方法、靜態塊是被哪一個線程調用的?
    • 線程類的構造方法、靜態塊是被new這個線程類所在的線程所調用的,而run方法裏面的代碼纔是被線程自身所調用的。
  • 舉個例子
    • 假設Thread2中new了Thread1,main函數中new了Thread2,那麼:
      • Thread2的構造方法、靜態塊是main線程調用的,Thread2的run()方法是Thread2本身調用的
      • Thread1的構造方法、靜態塊是Thread2調用的,Thread1的run()方法是Thread1本身調用的
  • 同步方法和同步塊,哪一個是更好的選擇?
    • 同步塊,這意味着同步塊以外的代碼是異步執行的,這比同步整個方法更提高代碼的效率。請知道一條原則:同步的範圍越小越好。
    • 技術博客大總結
  • 同步的範圍越少越好嗎?
    • 是的。雖然說同步的範圍越少越好,可是在Java虛擬機中仍是存在着一種叫作鎖粗化的優化方法,這種方法就是把同步範圍變大。這是有用的,比方說StringBuffer,它是一個線程安全的類,天然最經常使用的append()方法是一個同步方法,咱們寫代碼的時候會反覆append字符串,這意味着要進行反覆的加鎖->解鎖,這對性能不利,由於這意味着Java虛擬機在這條線程上要反覆地在內核態和用戶態之間進行切換,所以Java虛擬機會將屢次append方法調用的代碼進行一個鎖粗化的操做,將屢次的append的操做擴展到append方法的頭尾,變成一個大的同步塊,這樣就減小了加鎖-->解鎖的次數,有效地提高了代碼執行的效率。

5.0.1.9 synchonized(this)和synchonized(object)區別?Synchronize做用於方法和靜態方法區別?

  • synchonized(this)和synchonized(object)區別技術博客大總結
    • 其實並無很大的區別,synchonized(object)自己就包含synchonized(this)這種狀況,使用的場景都是對一個代碼塊進行加鎖,效率比直接在方法名上加synchonized高一些(下面分析),惟一的區別就是對象的不一樣。
    • 對synchronized(this)的一些理解
      • 1、當兩個併發線程訪問同一個對象object中的這個synchronized(this)同步代碼塊時,一個時間內只能有一個線程獲得執行。另外一個線程必須等待當前線程執行完這個代碼塊之後才能執行該代碼塊。
      • 2、然而,當一個線程訪問object的一個synchronized(this)同步代碼塊時,另外一個線程仍然能夠訪問該object中的非synchronized(this)同步代碼塊。
      • 3、尤爲關鍵的是,當一個線程訪問object的一個synchronized(this)同步代碼塊時,其餘線程對object中全部其它synchronized(this)同步代碼塊的訪問將被阻塞。
      • 4、當一個線程訪問object的一個synchronized(this)同步代碼塊時,它就得到了這個object的對象鎖。結果,其它線程對該object對象全部同步代碼部分的訪問都被暫時阻塞。
  • Synchronize做用於方法和靜態方法區別php

    • 測試代碼以下所示html

      private void test() {
      final TestSynchronized test1 = new TestSynchronized();
      final TestSynchronized test2 = new TestSynchronized();
      Thread t1 = new Thread(new Runnable() {
      
          @Override
          public void run() {
              test1.method01("a");
              //test1.method02("a");
          }
      });
      Thread t2 = new Thread(new Runnable() {
      
          @Override
          public void run() {
              test2.method01("b");
              //test2.method02("a");
          }
      });
      t1.start();
      t2.start();
      }

    private static class TestSynchronized{
    private int num1;
    public synchronized void method01(String arg) {
    try {
    if("a".equals(arg)){
    num1 = 100;
    System.out.println("tag a set number over");
    Thread.sleep(1000);
    }else{
    num1 = 200;
    System.out.println("tag b set number over");
    }
    System.out.println("tag = "+ arg + ";num ="+ num1);
    } catch (InterruptedException e) {
    e.printStackTrace();
    }
    }java

    private static int  num2;
    public static synchronized void method02(String arg) {
        try {
            if("a".equals(arg)){
                num2 = 100;
                System.out.println("tag a set number over");
                Thread.sleep(1000);
            }else{
                num2 = 200;
                System.out.println("tag b set number over");
            }
            System.out.println("tag = "+ arg + ";num ="+ num2);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    }git

    //調用method01方法打印日誌【普通方法】
    tag a set number over
    tag b set number over
    tag = b;num =200
    tag = a;num =100github

    //調用method02方法打印日誌【static靜態方法】
    tag a set number over
    tag = a;num =100
    tag b set number over
    tag = b;num =200面試

    - 在static方法前加synchronized:靜態方法屬於類方法,它屬於這個類,獲取到的鎖,是屬於類的鎖。 
    - 在普通方法前加synchronized:非static方法獲取到的鎖,是屬於當前對象的鎖。 [技術博客大總結](https://github.com/yangchong211/YCBlogs)
    - 結論:類鎖和對象鎖不一樣,synchronized修飾不加static的方法,鎖是加在單個對象上,不一樣的對象沒有競爭關係;修飾加了static的方法,鎖是加載類上,這個類全部的對象競爭一把鎖。

其餘介紹

01.關於博客彙總連接

02.關於個人博客

相關文章
相關標籤/搜索