1線程的創建方式
- 繼承Thread類
- 實現Runnable接口
- 實現Callable接口
2.這三種方式在項目中的使用有哪些,一般都是怎么用的
- 繼承thread類實現線程的方式通過實現run方法來實現線程,通過run進行線程的啟用
- 實現runnable方法實現run方法,然后通過thread類包裝并啟用
- 實現callable方法里的call方法,再由thread啟動
3.他們各自的優缺點
- thread:實現簡單,代碼簡潔,可以直接調用Thread類的方法,缺點是:單繼承限制,靈活性較低,且沒有返回值,無法獲取線程的執行結果
- runnable:避免了單繼承的限制,靈活性更高了,多個線程可以共享一個runnable,適合多線程操作共享資源,且runnable是一個函數接口,內存的開銷更小,缺點:無返回值,異常都是在方法內部拋出,代碼必須通過thread實現,代碼不太友好
- callable:callable有返回值,外部可以通過future.get()獲取異常,和runnable無單繼承的限制,多個callable可以共享資源,缺點:使用復雜,需要通過futureTask或者線程池啟動,用futureTak.get()方法會阻塞當前線程,直到線程結束,如果使用不當會導致性能問題,
4.技術的選型,三種分別應該怎么選
- 簡單任務且無需返回結果:優先用Runnable(避免單繼承限制)。
- 需要返回結果或處理異常:必須用Callable。
- 極少情況下(如無需繼承其他類且邏輯簡單)才用繼承Thread類。
5.線程池有哪些參數?
5個核心參數:核心線程數,最大線程數,空閑線程存活時間,阻塞任務隊列,拒絕策略
線程存貨時間單位,線程工廠
6.線程池的使用步驟
- 創建線程池對象,
- 實現線程
- 提交任務到線程池
- 關閉線程池
7.線程池有哪幾種?它們分別對應什么隊列?
- 固定大小線程池(核心線程數=最大線程數,空閑線程存活時間為0)=》無界隊列
特點:線程數量固定,不會動態增減,任務隊列無界可以無限接收任務(可能OOM - 單線程化線程池(核心線程數=最大線程數=1,空閑存活線程時間為0)=》無界隊列
- 可緩存線程池=》同步隊列
- 定時任務線程池=》延遲隊列
- 單線程定時任務線程池=》延遲隊列
無界隊列:隊列的容量沒有上限,可以一直添加任務(注意若任務提交速度遠快于處理速度,隊列會無限膨脹,可能導致內存溢出(OOM))
同步隊列:不存儲任何任務,生產即消費,
延遲隊列:隊列任務中任務按照延遲時間排序,只有當任務的延遲時間到期后才能被取出執行,
8.什么時候觸發最大線程條件?
核心線程全忙,隊列全滿,且最大線程數全滿(無界隊列不會觸發最大線程)
9.線程池拒絕策略有哪些?默認是哪個?
- AbortPolicy(默認,拋出RejectedExecutionException異常,適用場景:不允許任務丟失,需要明確感知到任務提交失敗的場景
- CallerRunsPolicy(讓提交的線程自己執行改任務,而非線程池中的線程執行,適用場景:需要減緩任務提交的速度,起到自我限流的作用
- discardpolicy:默認丟棄新提交的任務,不拋異常,也不做任何處理,適用日志,非關鍵數據的統計上報
- discardOldestPolicy:丟棄隊列中最舊的數據,騰出位置給新提交的任務,適用實時數據處理任務
10.什么是線程安全
解決共享資源的并發訪問沖突,常見的手段是關鍵字synchronized,和加鎖
11.如何判斷一個類是否線程安全?
- 共享資源的并發訪問沖突,
- 看線程是否有共享的資源(資源滿足原子性,可見性,有序性則為線程安全),是否是共享的狀態(有共享狀態且持有成員變量,且這個變量可能被多個線程讀寫,則線程不安全)
12.線程不安全的根源是什么?請舉例說明。
- 共享資源的非原子操作,具體來說就是,當共享資源被分為讀取,修改,寫入,三個操作不是原子性的時候,就有可能中間狀態被其他線程修改導致數據不一致
- 例如ArrayList就是線程不安全的:底層是elementData動態數組,當執行add(),remove()方法的時候不是原子性的
13.線程安全的三大特性(原子性、可見性、有序性)分別指什么?
原子性:操作不可拆分,要么全部執行,要么全部不執行
可見性:一個線程修改共享資源后,其他線程可以立即看見最新的值
有序性:程序執行順序符合預期(為提高性能jvm,cpu會對指令重排,在多線程環境下會影響線程之間的邏輯
14.ArrayList是否線程安全
底層是elementData動態數組,當執行add(),remove()方法的時候不是原子性的
add方法=》檢查容量-擴容-元素賦值
remove方法=》查找元素-移動后續元素填補空位-修改size
15.使用什么集合可以做到線程安全
- 線程安全的用Vector,使用synchronized修改,線程安全了,但是性能低于ArrayList(盡量不用
- 使用collection.synchronizedList()將ArrayList包裝為線程安全的集合(簡單場景且并發量低的情況下使用)
- CopyOnWriteArrayList,是juc并發包提供的線程安全集合,當進行修改的操作的時候,會創建底層的數據副本,修改完成后再替換原數組,讀操作無需加鎖(適合讀多寫少的情況)
16.如何線程安全地操作ArrayList
- 使用collections.synchronizedList()包裝修飾
- 使用copyonwriteArrayList()適合讀多寫少的情況,原理是采用讀寫分離,寫操作有數組復制的開銷,會造成內存占用的增加
17.ArrayList擴容機制
擴容依賴于兩個變量,elementData,size,是按需擴容
當調用add()方法的時候添加元素的時候,初始elementData為空則會觸發擴容,新容量為10,DEFAULT_CAPACITY默認值,然后每次到達上限的時候,擴充當前的1.5倍
18.HashMap、TreeMap、LinkedHashMap的區別?
hashMap:基于數組+鏈表/紅黑樹實現,無序插入,鍵可以允許插入null且只能一個;
treeMap:基于紅黑樹實現,按照鍵的自然順序或者自定義比較器排序,鍵不允許為null
linkedMap:基于哈希表+雙向鏈表實現,在hashMap的基礎上通過雙向鏈表記錄元素順序,即保留了hash表的查詢效率又保證了有序性,鍵允許為null
19.什么是紅黑樹
是一種簡單的自平衡二叉樹,通過一套規則進行保證樹的平衡,從而讓各種操作都能更高效的,
兩個核心特點:
- 節點非黑即紅且根節點必須是黑色的,
- 從任意節點到葉子的所有路徑黑色節點數量相等
20.他們各自適用的場景
hashmap:追求查詢和修改的高效性,且不關心元素順序(緩存,快速查詢)
treeMap:需要對鍵進行排序或者范圍查詢(排行榜,區間統計場景)
linkedHashMap:需要保留插入順序(日志記錄),需要實現LRU緩存(緩存淘汰策略)
21.HashMap數據結構、哈希沖突解決方法
數據結構是基于數組+鏈表/紅黑樹實現,
當多個鍵哈希到同一個桶的時候會鏈表的形式存儲這些鍵值對(解決哈希沖突
當鏈表過長時默認長度>8自動轉為紅黑樹,當紅黑樹的節點數少于6的時候會自動轉為鏈表
22.什么是哈希算法
把任意長度的輸入通過散列算法變為固定長度的輸出,hash表又是散列表
23.HashMap為什么線程不安全?如何線程安全地操作?
并發操作時可能導致數據不一致或結構破壞
hashmap多個線程同時進行put操作的會出現數據覆蓋的情況,導致數據的丟失
想要安全的操作線程可以使用concourrentHashMap,或者使用手動加鎖,collection.synchronizedMap, 手動加鎖并發效率低,會有性能問題,concurrentHashMap則是轉為高并發設計的
24.HashMap擴容的原理
判斷是否擴容的依據是元素數量>=閾值
分為兩個階段
1.計算新容量
2.遷移元素
25.Shiro怎么根據url對應權限,流程是什么?
1.請求攔截
2.匹配URL對的權限規則
3.檢驗用戶是否有該權限(通過dogetAuthorizationInfo獲取)
4.允許或者拒絕訪問
26.怎么結合redis存儲權限,提高性能的
一:
1.緩存用戶的所有權限,避免每次權限校驗的時候都查詢數據庫
2.緩存url與對應權限的對應關系,減少動態解析URL規則開銷
3.緩存角色與權限的映射關系,加速權限繼承關系的計算
27.session存放在哪里?
1.shiro默認把session存放在jvm中,— 缺點是集群環境下會話不共享,節點切換的時候需要重新登錄
重啟后session丟失,用戶需要重新登錄
內存容量有限用戶過多的情況下會oom
2.采用redis存儲,好處解決了會話共享和持久化,