接 Java面試集錦:25道線程類相關面試題與答案(一)java
1)原子性 原子性指的是一個或者多個操做,要麼所有執行而且在執行的過程當中不被其餘操做打斷,要麼就所有都不執行。git
2)可見性 可見性指多個線程操做一個共享變量時,其中一個線程對變量進行修改後,其餘線程能夠當即看到修改的結果。github
3)有序性 有序性,即程序的執行順序按照代碼的前後順序來執行。面試
因爲Java採用搶佔式的線程調度算法,所以可能會出現某條線程經常獲取到CPU控制權的狀況,爲了讓某些優先級比較低的線程也能獲取到CPU控制權,可使用Thread.sleep(0)手動觸發一次操做系統分配時間片的操做,這也是平衡CPU控制權的一種操做。算法
不是線程安全的操做。它涉及到多個指令,如讀取變量值,增長,而後存儲回內存,這個過程可能會出現多個線程交差編程
Callable規定(重寫)的方法是call(),Runnable規定(重寫)的方法是run()。 Callable的任務執行後可返回值,而Runnable的任務是不能返回值的。 Call方法能夠拋出異常,run方法不能夠。 運行Callable任務能夠拿到一個Future對象,表示異步計算的結果。它提供了檢查計算是否完成的方法,以等待計算的完成,並檢索計算的結果。經過Future對象能夠了解任務執行狀況,可取消任務的執行,還可獲取執行結果。安全
1)新建狀態(New):當線程對象對建立後,即進入了新建狀態,如:Thread t = new MyThread();微信
2)就緒狀態(Runnable):當調用線程對象的start()方法(t.start();),線程即進入就緒狀態。處於就緒狀態的線程,只是說明此線程已經作好了準備,隨時等待CPU調度執行,並非說執行了t.start()此線程當即就會執行;數據結構
3)運行狀態(Running):當CPU開始調度處於就緒狀態的線程時,此時線程才得以真正執行,即進入到運行狀態。注:就 緒狀態是進入到運行狀態的惟一入口,也就是說,線程要想進入運行狀態執行,首先必須處於就緒狀態中;多線程
4)阻塞狀態(Blocked):處於運行狀態中的線程因爲某種緣由,暫時放棄對CPU的使用權,中止執行,此時進入阻塞狀態,直到其進入到就緒狀態,才 有機會再次被CPU調用以進入到運行狀態。
根據阻塞產生的緣由不一樣,阻塞狀態又能夠分爲三種:
a.等待阻塞:運行狀態中的線程執行wait()方法,使本線程進入到等待阻塞狀態;
b.同步阻塞 – 線程在獲取synchronized同步鎖失敗(由於鎖被其它線程所佔用),它會進入同步阻塞狀態;
c.其餘阻塞 – 經過調用線程的sleep()或join()或發出了I/O請求時,線程會進入到阻塞狀態。當sleep()狀態超時、join()等待線程終止或者超時、或者I/O處理完畢時,線程從新轉入就緒狀態。
5)死亡狀態(Dead):線程執行完了或者因異常退出了run()方法,該線程結束生命週期。
1)重用存在的線程,減小對象建立銷燬的開銷。 2)可有效的控制最大併發線程數,提升系統資源的使用率,同時避免過多資源競爭,避免堵塞。 3)提供定時執行、按期執行、單線程、併發數控制等功能。
CountDownLatch CyclicBarrier Semaphore Exchanger
1)CountDownLatch簡單的說就是一個線程等待,直到他所等待的其餘線程都執行完成而且調用countDown()方法發出通知後,當前線程才能夠繼續執行。 2)cyclicBarrier是全部線程都進行等待,直到全部線程都準備好進入await()方法以後,全部線程同時開始執行! 3)CountDownLatch的計數器只能使用一次。而CyclicBarrier的計數器可使用reset() 方法重置。因此CyclicBarrier能處理更爲複雜的業務場景,好比若是計算髮生錯誤,能夠重置計數器,並讓線程們從新執行一次。 4)CyclicBarrier還提供其餘有用的方法,好比getNumberWaiting方法能夠得到CyclicBarrier阻塞的線程數量。isBroken方法用來知道阻塞的線程是否被中斷。若是被中斷返回true,不然返回false。
在Java中,synchronized關鍵字是用來控制線程同步的,就是在多線程的環境下,控制synchronized代碼段不被多個線程同時執行。 synchronized既能夠加在一段代碼上,也能夠加在方法上。
對於可見性,Java提供了volatile關鍵字來保證可見性。 當一個共享變量被volatile修飾時,它會保證修改的值會當即被更新到主存,當有其餘線程須要讀取時,它會去內存中讀取新值。 從實踐角度而言,volatile的一個重要做用就是和CAS結合,保證了原子性,詳細的能夠參見java.util.concurrent.atomic包下的類,好比AtomicInteger。
CAS是compare and swap的縮寫,即咱們所說的比較交換。 cas是一種基於鎖的操做,並且是樂觀鎖。在java中鎖分爲樂觀鎖和悲觀鎖。悲觀鎖是將資源鎖住,等一個以前得到鎖的線程釋放鎖以後,下一個線程才能夠訪問。而樂觀鎖採起了一種寬泛的態度,經過某種方式不加鎖來處理資源,好比經過給記錄加version來獲取數據,性能較悲觀鎖有很大的提升。 CAS 操做包含三個操做數 —— 內存位置(V)、預期原值(A)和新值(B)。若是內存地址裏面的值和A的值是同樣的,那麼就將內存裏面的值更新成B。CAS是經過無限循環來獲取數據的,若果在第一輪循環中,a線程獲取地址裏面的值被b線程修改了,那麼a線程須要自旋,到下次循環纔有可能機會執行。 java.util.concurrent.atomic 包下的類大可能是使用CAS操做來實現的( AtomicInteger,AtomicBoolean,AtomicLong)。
1)CAS容易形成ABA問題 一個線程a將數值改爲了b,接着又改爲了a,此時CAS認爲是沒有變化,實際上是已經變化過了,而這個問題的解決方案可使用版本號標識,每操做一次version加1。在java5中,已經提供了AtomicStampedReference來解決問題。
2) 不能保證代碼塊的原子性 CAS機制所保證的知識一個變量的原子性操做,而不能保證整個代碼塊的原子性。好比須要保證3個變量共同進行原子性的更新,就不得不使用synchronized了。
3)CAS形成CPU利用率增長 以前說過了CAS裏面是一個循環判斷的過程,若是線程一直沒有獲取到狀態,cpu資源會一直被佔用。
在併發編程中,咱們常常用到非阻塞的模型,在以前的多線程的三種實現中,無論是繼承thread類仍是實現runnable接口,都沒法保證獲取到以前的執行結果。經過實現Callback接口,並用Future能夠來接收多線程的執行結果。 Future表示一個可能尚未完成的異步任務的結果,針對這個結果能夠添加Callback以便在任務執行成功或失敗後做出相應的操做。
AQS是AbustactQueuedSynchronizer
的簡稱,它是一個Java提升的底層同步工具類,用一個int類型的變量表示同步狀態,並提供了一系列的CAS操做來管理這個同步狀態。 AQS是一個用來構建鎖和同步器的框架,使用AQS能簡單且高效地構造出應用普遍的大量的同步器,好比咱們提到的ReentrantLock,Semaphore,其餘的諸如ReentrantReadWriteLock,SynchronousQueue,FutureTask等等皆是基於AQS的。
1)獨佔式 2)共享式
這樣方便使用者實現不一樣類型的同步組件,獨佔式如ReentrantLock,共享式如Semaphore,CountDownLatch,組合式的如ReentrantReadWriteLock。總之,AQS爲使用提供了底層支撐,如何組裝實現,使用者能夠自由發揮。
首先明確一下,不是說ReentrantLock很差,只是ReentrantLock某些時候有侷限。若是使用ReentrantLock,可能自己是爲了防止線程A在寫數據、線程B在讀數據形成的數據不一致,但這樣,若是線程C在讀數據、線程D也在讀數據,讀數據是不會改變數據的,沒有必要加鎖,可是仍是加鎖了,下降了程序的性能。 由於這個,才誕生了讀寫鎖ReadWriteLock。ReadWriteLock是一個讀寫鎖接口,ReentrantReadWriteLock是ReadWriteLock接口的一個具體實現,實現了讀寫的分離,讀鎖是共享的,寫鎖是獨佔的,讀和讀之間不會互斥,讀和寫、寫和讀、寫和寫之間纔會互斥,提高了讀寫的性能。
這個其實前面有提到過,FutureTask表示一個異步運算的任務。FutureTask裏面能夠傳入一個Callable的具體實現類,能夠對這個異步運算的任務的結果進行等待獲取、判斷是否已經完成、取消任務等操做。固然,因爲FutureTask也是Runnable接口的實現類,因此FutureTask也能夠放入線程池中。
synchronized是和if、else、for、while同樣的關鍵字,ReentrantLock是類,這是兩者的本質區別。既然ReentrantLock是類,那麼它就提供了比synchronized更多更靈活的特性,能夠被繼承、能夠有方法、能夠有各類各樣的類變量,ReentrantLock比synchronized的擴展性體如今幾點上:
1)ReentrantLock能夠對獲取鎖的等待時間進行設置,這樣就避免了死鎖 2)ReentrantLock能夠獲取各類鎖的信息 3)ReentrantLock能夠靈活地實現多路通知
另外,兩者的鎖機制其實也是不同的。ReentrantLock底層調用的是Unsafe的park方法加鎖,synchronized操做的應該是對象頭中mark word,這點我不能肯定。
synchronized是悲觀鎖,屬於搶佔式,會引發其餘線程阻塞。 volatile提供多線程共享變量可見性和禁止指令重排序優化。 CAS是基於衝突檢測的樂觀鎖(非阻塞)
這個問題常問,sleep方法和wait方法均可以用來放棄CPU必定的時間,不一樣點在於若是線程持有某個對象的監視器,sleep方法不會放棄這個對象的監視器,wait方法會放棄這個對象的監視器
ThreadLocal
是一個本地線程副本變量工具類。主要用於將私有線程和該線程存放的副本對象作一個映射,各個線程之間的變量互不干擾,在高併發場景下,能夠實現無狀態的調用,特別適用於各個線程依賴不通的變量值完成操做的場景。
簡單說ThreadLocal
就是一種以空間換時間的作法,在每一個Thread
裏面維護了一個以開地址法實現的ThreadLocal.ThreadLocalMap
,把數據進行隔離,數據不共享,天然就沒有線程安全方面的問題了。
若是這個異常沒有被捕獲的話,這個線程就中止執行了。另外重要的一點是:若是這個線程持有某個某個對象的監視器,那麼這個對象監視器會被當即釋放
線程調度器選擇優先級最高的線程運行,可是,若是發生如下狀況,就會終止線程的運行:
1)線程體中調用了yield方法讓出了對cpu的佔用權利 2)線程體中調用了sleep方法使線程進入睡眠狀態 3)線程因爲IO操做受到阻塞 4)另一個更高優先級線程出現 5)在支持時間片的系統中,該線程的時間片用完
若是線程是由於調用了wait()、sleep()或者join()方法而致使的阻塞,能夠中斷線程,而且經過拋出InterruptedException來喚醒它;若是線程遇到了IO阻塞,無能爲力,由於IO是操做系統實現的,Java代碼並無辦法直接接觸到操做系統。
大廠筆試內容集合(內有詳細解析) 持續更新中....
歡迎關注我的微信公衆號:Coder編程 歡迎關注Coder編程公衆號,主要分享數據結構與算法、Java相關知識體系、框架知識及原理、Spring全家桶、微服務項目實戰、DevOps實踐之路、每日一篇互聯網大廠面試或筆試題以及PMP項目管理知識等。更多精彩內容正在路上~ 新建了一個qq羣:315211365,歡迎你們進羣交流一塊兒學習。謝謝了!也能夠介紹給身邊有須要的朋友。
文章收錄至 Github: github.com/CoderMerlin… Gitee: gitee.com/573059382/c… 歡迎關注並star~