面試高併發,看這篇就夠了(上)
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方法,優雅關閉任務,這時將不再接受新的任務,同時等待已提交的任務執行完成。
喜歡請關注微信公眾號:碼農小麥。