Java面試集錦:25道線程類相關面試題與答案(二)

Java面試集錦:25道線程類相關面試題與答案(二)

Java面試集錦:25道線程類相關面試題與答案(二)

接Java面試集錦:25道線程類相關面試題與答案(一)

26. 併發編程三要素?

1)原子性
原子性指的是一個或者多個操作,要麼全部執行並且在執行的過程中不被其他操作打斷,要麼就全部都不執行。

2)可見性
可見性指多個線程操作一個共享變量時,其中一個線程對變量進行修改後,其他線程可以立即看到修改的結果。

3)有序性
有序性,即程序的執行順序按照代碼的先後順序來執行。

27. Thread.sleep(0)的作用是什麼?

由於Java採用搶佔式的線程調度算法,因此可能會出現某條線程常常獲取到CPU控制權的情況,為了讓某些優先級比較低的線程也能獲取到CPU控制權,可以使用Thread.sleep(0)手動觸發一次操作系統分配時間片的操作,這也是平衡CPU控制權的一種操作。

28. java中的++操作符線程安全麼??

不是線程安全的操作。它涉及到多個指令,如讀取變量值,增加,然後存儲回內存,這個過程可能會出現多個線程交差

29. Runnable和Callable有什麼區別?

Callable規定(重寫)的方法是call(),Runnable規定(重寫)的方法是run()。
Callable的任務執行後可返回值,而Runnable的任務是不能返回值的。
Call方法可以拋出異常,run方法不可以。
運行Callable任務可以拿到一個Future對象,表示異步計算的結果。它提供了檢查計算是否完成的方法,以等待計算的完成,並檢索計算的結果。通過Future對象可以瞭解任務執行情況,可取消任務的執行,還可獲取執行結果。

30. 介紹一下線程中的五種基本狀態:

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()方法,該線程結束生命週期。

31. 線程池的優點?

1)重用存在的線程,減少對象創建銷燬的開銷。
2)可有效的控制最大併發線程數,提高系統資源的使用率,同時避免過多資源競爭,避免堵塞。
3)提供定時執行、定期執行、單線程、併發數控制等功能。

32. 常用的併發工具類有哪些?

CountDownLatch
CyclicBarrier
Semaphore
Exchanger

33. CyclicBarrier和CountDownLatch的區別

1)CountDownLatch簡單的說就是一個線程等待,直到他所等待的其他線程都執行完成並且調用countDown()方法發出通知後,當前線程才可以繼續執行。


2)cyclicBarrier是所有線程都進行等待,直到所有線程都準備好進入await()方法之後,所有線程同時開始執行!
3)CountDownLatch的計數器只能使用一次。而CyclicBarrier的計數器可以使用reset() 方法重置。所以CyclicBarrier能處理更為複雜的業務場景,比如如果計算發生錯誤,可以重置計數器,並讓線程們重新執行一次。
4)CyclicBarrier還提供其他有用的方法,比如getNumberWaiting方法可以獲得CyclicBarrier阻塞的線程數量。isBroken方法用來知道阻塞的線程是否被中斷。如果被中斷返回true,否則返回false。

34. synchronized的作用?

在Java中,synchronized關鍵字是用來控制線程同步的,就是在多線程的環境下,控制synchronized代碼段不被多個線程同時執行。
synchronized既可以加在一段代碼上,也可以加在方法上。

35. volatile關鍵字的作用是什麼?

對於可見性,Java提供了volatile關鍵字來保證可見性。
當一個共享變量被volatile修飾時,它會保證修改的值會立即被更新到主存,當有其他線程需要讀取時,它會去內存中讀取新值。
從實踐角度而言,volatile的一個重要作用就是和CAS結合,保證了原子性,詳細的可以參見java.util.concurrent.atomic包下的類,比如AtomicInteger。

36.什麼是CAS?

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)。

37. CAS會出現哪些問題?

1)CAS容易造成ABA問題
一個線程a將數值改成了b,接著又改成了a,此時CAS認為是沒有變化,其實是已經變化過了,而這個問題的解決方案可以使用版本號標識,每操作一次version加1。在java5中,已經提供了AtomicStampedReference來解決問題。

2) 不能保證代碼塊的原子性
CAS機制所保證的知識一個變量的原子性操作,而不能保證整個代碼塊的原子性。比如需要保證3個變量共同進行原子性的更新,就不得不使用synchronized了。

3)CAS造成CPU利用率增加
之前說過了CAS裡面是一個循環判斷的過程,如果線程一直沒有獲取到狀態,cpu資源會一直被佔用。

38. 什麼是Future?

在併發編程中,我們經常用到非阻塞的模型,在之前的多線程的三種實現中,不管是繼承thread類還是實現runnable接口,都無法保證獲取到之前的執行結果。通過實現Callback接口,並用Future可以來接收多線程的執行結果。
Future表示一個可能還沒有完成的異步任務的結果,針對這個結果可以添加Callback以便在任務執行成功或失敗後作出相應的操作。

39. 什麼是AQS?

AQS是AbustactQueuedSynchronizer的簡稱,它是一個Java提高的底層同步工具類,用一個int類型的變量表示同步狀態,並提供了一系列的CAS操作來管理這個同步狀態。


AQS是一個用來構建鎖和同步器的框架,使用AQS能簡單且高效地構造出應用廣泛的大量的同步器,比如我們提到的ReentrantLock,Semaphore,其他的諸如ReentrantReadWriteLock,SynchronousQueue,FutureTask等等皆是基於AQS的。

40. AQS支持兩種同步方式:

1)獨佔式
2)共享式

這樣方便使用者實現不同類型的同步組件,獨佔式如ReentrantLock,共享式如Semaphore,CountDownLatch,組合式的如ReentrantReadWriteLock。總之,AQS為使用提供了底層支撐,如何組裝實現,使用者可以自由發揮。

41. ReadWriteLock是什麼?

首先明確一下,不是說ReentrantLock不好,只是ReentrantLock某些時候有侷限。如果使用ReentrantLock,可能本身是為了防止線程A在寫數據、線程B在讀數據造成的數據不一致,但這樣,如果線程C在讀數據、線程D也在讀數據,讀數據是不會改變數據的,沒有必要加鎖,但是還是加鎖了,降低了程序的性能。
因為這個,才誕生了讀寫鎖ReadWriteLock。ReadWriteLock是一個讀寫鎖接口,ReentrantReadWriteLock是ReadWriteLock接口的一個具體實現,實現了讀寫的分離,讀鎖是共享的,寫鎖是獨佔的,讀和讀之間不會互斥,讀和寫、寫和讀、寫和寫之間才會互斥,提升了讀寫的性能。

42. FutureTask是什麼

這個其實前面有提到過,FutureTask表示一個異步運算的任務。FutureTask裡面可以傳入一個Callable的具體實現類,可以對這個異步運算的任務的結果進行等待獲取、判斷是否已經完成、取消任務等操作。當然,由於FutureTask也是Runnable接口的實現類,所以FutureTask也可以放入線程池中。

43. synchronized和ReentrantLock的區別?

synchronized是和if、else、for、while一樣的關鍵字,ReentrantLock是類,這是二者的本質區別。既然ReentrantLock是類,那麼它就提供了比synchronized更多更靈活的特性,可以被繼承、可以有方法、可以有各種各樣的類變量,ReentrantLock比synchronized的擴展性體現在幾點上:

1)ReentrantLock可以對獲取鎖的等待時間進行設置,這樣就避免了死鎖
2)ReentrantLock可以獲取各種鎖的信息
3)ReentrantLock可以靈活地實現多路通知

另外,二者的鎖機制其實也是不一樣的。ReentrantLock底層調用的是Unsafe的park方法加鎖,synchronized操作的應該是對象頭中mark word,這點我不能確定。

44. synchronized、volatile、CAS比較

synchronized是悲觀鎖,屬於搶佔式,會引起其他線程阻塞。
volatile提供多線程共享變量可見性和禁止指令重排序優化。
CAS是基於衝突檢測的樂觀鎖(非阻塞)

45. sleep方法和wait方法有什麼區別?

這個問題常問,sleep方法和wait方法都可以用來放棄CPU一定的時間,不同點在於如果線程持有某個對象的監視器,sleep方法不會放棄這個對象的監視器,wait方法會放棄這個對象的監視器

46. ThreadLocal是什麼?有什麼用?

ThreadLocal是一個本地線程副本變量工具類。主要用於將私有線程和該線程存放的副本對象做一個映射,各個線程之間的變量互不干擾,在高併發場景下,可以實現無狀態的調用,特別適用於各個線程依賴不通的變量值完成操作的場景。

簡單說ThreadLocal就是一種以空間換時間的做法,在每個Thread裡面維護了一個以開地址法實現的ThreadLocal.ThreadLocalMap,把數據進行隔離,數據不共享,自然就沒有線程安全方面的問題了。

47.一個線程如果出現了運行時異常怎麼辦?

如果這個異常沒有被捕獲的話,這個線程就停止執行了。另外重要的一點是:如果這個線程持有某個某個對象的監視器,那麼這個對象監視器會被立即釋放

48. 線程的調度策略

線程調度器選擇優先級最高的線程運行,但是,如果發生以下情況,就會終止線程的運行:

1)線程體中調用了yield方法讓出了對cpu的佔用權利
2)線程體中調用了sleep方法使線程進入睡眠狀態
3)線程由於IO操作受到阻塞
4)另外一個更高優先級線程出現
5)在支持時間片的系統中,該線程的時間片用完

49. 怎麼喚醒一個阻塞的線程?

如果線程是因為調用了wait()、sleep()或者join()方法而導致的阻塞,可以中斷線程,並且通過拋出InterruptedException來喚醒它;如果線程遇到了IO阻塞,無能為力,因為IO是操作系統實現的,Java代碼並沒有辦法直接接觸到操作系統。

50. 你有哪些多線程開發良好的實踐?

  • 給線程命名
  • 最小化同步範圍
  • 優先使用volatile
  • 儘可能使用更高層次的併發工具而非wait和notify()來實現線程通信,如BlockingQueue,Semeaphore
  • 優先使用併發容器而非同步容器.
  • 考慮使用線程池

推薦

大廠筆試內容集合(內有詳細解析) 持續更新中….

ProcessOn是一個在線作圖工具的聚合平臺~

文末

歡迎關注個人微信公眾號:Coder編程
歡迎關注Coder編程公眾號,主要分享數據結構與算法、Java相關知識體系、框架知識及原理、Spring全家桶、微服務項目實戰、DevOps實踐之路、每日一篇互聯網大廠面試或筆試題以及PMP項目管理知識等。更多精彩內容正在路上~

Java面試集錦:25道線程類相關面試題與答案(二)


分享到:


相關文章: