面試高並發,看這篇就夠了(上)


1 java中常見的同步機制?

java主要同步機制是synchronized關鍵字, 還有顯式的Lock,volatile,atomic,還有一些同步集合、阻塞隊列等。

2 ‘++’操作是線程安全的嗎?

不是,它是由讀取-修改-寫入三個獨立的操作組成。

3 共享變量在多線程下如何保證線程安全?

因為多線程是交替執行,每個線程操作共享變量時可能會導致數據不一致,要確保線程安全,需要在訪問共享變量時添加同步機制。當然,如果這個變量本身是線程安全的,比如AtomicLong,那么多線程訪問也是安全的。

4 是否共享變量都使用類似AtomicLong原子安全類,多線程訪問就是安全的?

這個不確定,因為無法保證多個變量同時操作,一個原子變量可以保證自己的安全性,但是同時操作多個有邏輯依賴原子的變量,仍可能帶來線程安全問題。單個安全不代表組合也安全。

5 synchronized鎖的使用?

synchronized同步代碼塊,修飾的方法是整個方法體,同步代碼塊的鎖就是方法調用所在的對象實例;靜態的synchronized方法以Class對象作為鎖;還可以修飾具體對象,以具體對象為鎖。

6 可重入鎖,synchronized是可重入的嗎?

當一個線程擁有對象鎖之后,如果再次訪問此對象的同步代碼塊或對象時,不再需要獲取鎖,這就是可重入鎖;synchronized是可重入的。

7 什么是重排序?

編譯器、處理器及運行時都有可能對操作的執行順序進行一些意想不到的調整。

8 volatile關鍵字的理解?

volatile屬於一個輕量的同步原語,被它修飾的變量,變量的更新操作可以通知到其他線程。簡單來說,就是volatile修飾的變量不會被執行重排序,所以保證了變量的可見性。但volatile無法保證變量的原子性操作,仍然是不安全的,通常可以用作標志位,如退出線程的循環變量。

9 final修飾的不可變對象?

由關鍵字final修飾的對象是不可變的,不能被重新賦值,但是final仍可以修飾可變對象的引用,例如集合:final修飾的集合本身引用地址不能改變,但是集合內的數據還是可以修改的。不可變對象會減少加鎖或保護性副本的需求,可以帶來一些性能上的優勢。

10 常見的並發容器?

ConcurrentHashMap:使用了分段鎖,鎖的粒度變得更小,多線程訪問時,可能都不存在鎖的競爭,所以大大提高了吞吐量。簡單對比來看,就好比數據庫上用行鎖來取代表鎖,行鎖無疑帶來更大的並發。

CopyOnWriteArrayList:寫入時復制,多線程訪問時,彼此不會互相干擾或被修改的線程所干擾,當然copy時有開銷的,尤其時列表元素龐大,且寫入操作頻繁時,所以僅當迭代操作遠遠大於修改操作時,才應該考慮使用。

BlockingQueue:阻塞隊列提供了可阻塞的put和take方法,當隊列已經滿了,那么put操作將阻塞到隊列可用,當隊列為空時,take操作會阻塞到隊列里有數據。有界的隊列是一種強大的資源管理器,可以在程序負荷過載時保護應用,可作為一種服務降級的策略。阻塞隊列還提供offer操作,當數據無法加入隊列時,返回失敗狀態,給應用主動處理負荷過載帶來更多靈活性。

11 常見的同步工具類?

CountDownLatch:遞減計數器閉鎖,直到達到某個條件時才放行,多線程可以調用await方法一直阻塞,直到計數器遞減為零。比如我們連接zookeeper,由於連接操作是異步的,所以可以使用countDownLatch創建一個計數器為1的鎖,連接掛起,當異步連接成功時,調用countDown通知掛起線程;再比如5V5游戲競技,只有房間人滿了才可以開始游戲。

FutureTask:帶有計算結果的任務,在計算完成時才能獲取結果,如果計算尚未完成,則阻塞 get 方法。FutureTask將計算結果從執行線程傳遞到獲取這個結果的線程。

Semaphore:信號量,用來控制同時訪問某個特定資源的數量,只有獲取到許可acquire,才能夠正常執行,並在完成后釋放許可,acquire會一致阻塞到有許可或中斷超時。使用信號量可以輕松實現一個阻塞隊列。

CyclicBarrier:類似於閉鎖,它可以阻塞一組線程,只有所有線程全部到達以后,才能夠繼續執行,so線程必須相互等待。這在並行計算中是很有用的,將一個問題拆分為多個獨立的子問題,當線程到達柵欄時,調用await等待,一直阻塞到所有參與線程全部到達,再執行下一步任務。

12 什么是Executor?

Executor執行已提交的Runnable任務,把任務和執行機制分離開來,可以看作是一個任務執行框架,任務與執行的解耦,可以更靈活的指定執行方式。所以應該使用Executor代替new Thread(Runnable).start()。

13 什么是線程池,如何創建線程池?

線程池就是由一組活躍的線程集合,由於線程的創建與銷毀開銷都比較大,所以利用線程池減少線程的性能開銷,提高響應性。適當的調節線程池大小,可以有效利用處理器,同時防止過多線程相互競爭資源耗費內存。使用Executors可以很方便的創建線程池。

14 Executors可以創建哪些類型的線程池?

newFixedThreadPool:創建一個固定大小的線程池,每當提交一個任務就創建一個線程,直到達到最大數量,達到最大線程數后線程規模不再變化。

newCachedThreadPool:創建一個可以根據需要創建新線程的線程池,當線程規模大於處理請求時,將回收空閑線程,當需求增加時,可以添加新的線程。

newSingleThreadExecutor:創建一個單線程Executor。

newScheduledThreadPool:創建一個固定長度,以延遲或定時的執行方式執行任務。

15 Executor的生命周期?

ExcutorService擴展了Executor,有三種狀態:運行,關閉,已終止。可以調用shutdown方法,優雅關閉任務,這時將不再接受新的任務,同時等待已提交的任務執行完成。

喜歡請關注微信公眾號:碼農小麥


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM