1.什么是同步容器?同步容器使用什么方式實現線程安全
同步容器可以簡單的理解位通過synchronized來實現同步的容器,如果有多個線程調用同步容器的方法,它們將會串行執行。包括Vector和Hashtable,以及由同步容器封裝類。Collections.synchronizedXxx等工廠方法創建的類。
同步容器實現線程安全的方式將它們的狀態封裝起來,并對每個公有方法同步,使得每次只有一個線程能夠訪問容器的狀態。
注意通過在迭代期間持有Vector的鎖,可以防止其他線程在迭代期間修改Vector,但也因此降低了并發性。
2.導致拋出ConcurrentModificationException異常的原因?
首先,對容器類進行迭代的標準方式就是使用Iterator,但在設計同步容器類的迭代器時并沒有考慮到并發修改的問題,并且它們表現出的行為是”及時失敗“(fail-fast)的。這就意味著,當它們發現容器在迭代過程中被修改時,就會拋出一個ConcurrentModificationException異常。
其次,這種”及時失敗“的迭代器并不是一種完備的處理機制,只是”善意地“捕獲并發錯誤,因此只能作為并發問題的預警指示器。
最后,如果不希望在迭代期間對容器加鎖,那么一種替代的方式就是”克隆“容器,并在副本上進行迭代。
3.經典并發容器類的介紹
通過并發容器來代替同步容器,可以極大地提高伸縮性并降低風險。
ConcurrentHashMap:基于散列的Map,并不是將每個方法都在同一個鎖上同步使得每次只能有一個線程訪問線程,而使用一種更細粒度的加鎖機制來實現更大程度的共享。這種機制稱為分段鎖(Lock Striping)。在這種機制中,任意數量的讀取線程可以并發地訪問map,執行讀取操作的線程和執行寫入操作的線程可以并發地訪問map,并且一定數量的寫入線程可以并發地修改map。ConcurrentHashMap帶來的結果是,在并發訪問的環境下將實現更高的吞吐量,而在單線程環境中只損失非常小的性能。
ConcurrentHashMap與其他并發容器一起增強了同步容器類:它們提供的迭代器不會拋出ConcurrentModificationException,因此不需要在迭代過程中對容器加鎖。ConcurrentHashMap返回的迭代器具有弱一致性。弱一致性的迭代器可以容忍并發的修改,當創建迭代器時會遍歷已有的元素,并可以(但是不保證)在迭代器被構造后將修改操作反應給容器。
CopyOnWriteArrayList用于替代同步List,在迭代期間不需要對容器進行加鎖或復制。“寫入時復制(Copy-On-Write)”容器的線程安全性在于,只要是正確發布一個事實不可變的對象,那么在訪問該對象時就不再需要進一步的同步。在每次修改時,都會創建并重新發布一個新的容器副本,從而實現可變性。
注意:僅當迭代的操作遠遠多于修改操作時,才應該使用“寫入時復制“的容器。
4.阻塞隊列和生產者-消費者模式
阻塞隊列分為有界隊列和無界隊列,支持阻塞的put、take和定時的offer和poll方法。常見的生產者-消費者設計模式就是線程池和工作隊列的組合。
offer方法:如果數據項不能夠被添加到隊列中,那么將返回一個失敗的狀態而不是阻塞直至可以添加。這個方法的使用可以提供更靈活的方式來處理負載過荷的情況,例如減輕負載,將多余的工作項序列化并寫入磁盤,減少生產者的數量,或者通過某種方式來抑制生產者線程。
5.雙端隊列與工作密取(work stealing)
在工作密取的設計中,每個消費者都有自己的雙端隊列。如果一個消費者完成自己雙端隊列中的全部工作,那么它可以從其他的消費者雙端隊列秘密獲取工作。
優勢:比傳統的生產者-消費者模式具有更高的伸縮性,減少競爭的發生。
6.阻塞產生的原因?中斷異常出現的處理方式?
導致線程阻塞的原因包括:等待I/O操作結束,等待獲取一個鎖,等待從一個阻塞的方法中醒過來(thread.sleep),或者等待另一個線程的計算機結果。
當代碼調用中拋出一個InterruptedException異常的方法時,基本有兩種主要選擇方案:
傳遞InterruptedException。將異常的處理權傳遞給方法的調用者(可以捕獲異常,然后執行某種清理的操作再傳遞給上層)。
恢復中斷。
屏蔽中斷:即對Thread進行擴展,并且控制調用棧上所有更高層次的代碼。
7.同步工具類
同步工具類包含一些特定的結構化屬性:它們封裝了一些狀態,這些狀態將決定執行同步工具類的線程是繼續執行還是等待。包括信號量(Semaphore)、柵欄(Barrier)、以及閉鎖(Latch)。
閉鎖
? 閉鎖可以用來確保某些活動直到其他活動都完成后才繼續執行。當閉鎖到達結束狀態后,將不會再改變狀態。
? 啟動門將使主線程能夠同時釋放所有工作線程,而結束們則是主線程能夠等待最后一個線程執行完成,而不是順序地等待每個線程執行完成。
?注意: FutureTask也可以用作閉鎖,并且可以處于三種狀態:等待運行、正在運行和運行完成。
信號量
? 計數信號量用來控制同時訪問某個特定資源的操作數量,或者同時執行某個指定操作的數量。計數信號量還可以用來實現某種資源池,或者對容器施加邊界。
?注意: 計數信號量的特殊情況二值信號量,即初始值為1的semaphore,二值信號量可以用作互斥體,并且具備不可重入的加鎖語義:誰擁有這個唯一的許可,誰就擁有了互斥體。
柵欄
柵欄類似于閉鎖,它能阻塞一組線程直到某個事情發生。柵欄與閉鎖的關鍵區別在于,所有線程必須同時到達柵欄的位置,才能繼續執行。閉鎖用于等待事情,而柵欄用于等待其他線程。柵欄可被重用。
8.如何構建緩存的學習?
為什么要使用緩存?
? ? 緩沖可以縮短響應時間,減少不必要的重復操作。
設計緩沖的幾點注意事項?
? ? ?是否是線程安全的,是否是原子操作。
? ? ?有沒有緩存污染的情況,對于失敗的緩存能否有效的清除。