多線程相關(guān)
死鎖
死鎖四個條件:
-
互斥條件
- 臨界資源只能被一個線程擁有
-
占有且等待
- 線程/進程擁有補發(fā)臨界資源,但同時請求另外一些臨界資源
-
占有不釋放
- 在請求到其它臨界資源前,已擁有的臨界資源不會被釋放
-
循環(huán)鏈
- 多個線程/進程形成循環(huán)請求鏈
饑餓
線程一直不能獲得所需要的資源,如:低優(yōu)先級線程請求資源一直被高優(yōu)先級線程持有
多線程操作特性
-
原子性
- 操作不可中斷
-
有序性
-
多線程操作是有序的,但可能出現(xiàn)cpu指令重排:
- cpu采用流水線指令模式,可能出現(xiàn)后面指令提前到前面執(zhí)行
-
-
可見性
線程對臨界資源的操作,其他線程能看到最新的臨界資源
-
violate
保證有序性和可見性
臨界資源發(fā)生變化時,強迫寫到共享內(nèi)存
線程狀態(tài)
-
新建
- new
-
就緒
start:等待系統(tǒng)調(diào)度運行
yield:從運行切換到就緒,重新參與資源競爭
-
運行
- run
-
阻塞
join
sleep
suspend
-
結(jié)束
正常運行結(jié)束
stop: 不建議使用
-
中斷
并不會中斷線程,只是設(shè)置中斷標識位
通過中斷,使線程跳出阻塞狀態(tài)(sleep等)
線程并發(fā)包:java.util.concurrent
-
synchronized
-
重入鎖
- 線程可重復(fù)訪問已獲得的臨界資源,無需釋放和再申請鎖
-
配合object.wait,object.notify實現(xiàn)多線程通信
使用前需獲得鎖
wait執(zhí)行完后自動釋放鎖
notify/notifyall不會釋放鎖,只是發(fā)送一個通知,當同步塊執(zhí)行完后才會釋放鎖
-
-
重入鎖:reentrantlock
重入鎖
可設(shè)置優(yōu)先響應(yīng)中斷,獲得鎖的過程中,可優(yōu)先響應(yīng)中斷,避免死鎖
可設(shè)置超時時長:獲取的過程中,可設(shè)置等待時長
-
可設(shè)置是否為公平鎖
公平鎖:按申請的先后順序獲得鎖
不公平鎖:隨機分配
可與condition(notify/signal)配合使用,實現(xiàn)多線程通信,類似synchronized+object.wait+object.notify
-
信號量:semaphone
- 實現(xiàn)多個線程同時訪問臨界資源
-
讀寫鎖:writereadlock
讀不阻塞
寫阻塞
-
計時器:countdownlatch
- 計數(shù)為0時,線程訪問臨界資源,跳出阻塞
-
循環(huán)柵欄:cyclebarrier
- 類似countdownlatch,但可重復(fù)計數(shù)
-
locksupport
可實現(xiàn)線程的暫停和回復(fù):park、unpark
即使先unpark在park也無問題,不像suppend和resume,resume發(fā)生在suppend前出問題
線程并發(fā)集合
concurrentmap
collections集合方法
copyandwritelist
blockingqueue
線程池
創(chuàng)建線程池的幾個參數(shù):
corepoolsize:核心線程數(shù)
maxnumpoolsize:最大線程數(shù)
keepalive:閑置線程(非核心線程)存活時間
unit:存活時間單位
-
workqueue:當無空閑線程處理任務(wù)時,任務(wù)放到該工作隊列等待調(diào)度
- BlockingQueue實例,不同實例對應(yīng)了不同調(diào)度策略和拒絕策略
threadfactory:線程創(chuàng)建工廠
handler:任務(wù)拒絕執(zhí)行策略
CAS
比較并交換/無鎖/樂觀鎖
-
a-b-a問題
線程x讀取遍歷z的值為a,線程y讀取到z的值也為a,但此時z的值可能已經(jīng)經(jīng)過多次變化
可通過版本號優(yōu)化
-
AutoInteger等
- 內(nèi)部采用死循環(huán)獲取臨界資源方式,效率低
鎖優(yōu)化
鎖持有時間
加鎖粒度:細化、粗化
鎖分離
JVM
參考:https://juejin.im/post/5b85ea54e51d4538dd08f601
java內(nèi)存劃分
-
堆
- 線程共享
-
方法區(qū)
- 線程共享
native棧
java棧
pc
java堆劃分
-
新生代minor
-
eden區(qū)
對象一般分配在新生代的eden區(qū),大對象和長期存活對象分配在老年代
eden區(qū)對象經(jīng)過一次gc后,將移動到surrivor區(qū),surrivor區(qū)經(jīng)過n次(一般為15次)gc后,將移動到老年代
-
surrivor區(qū)
from區(qū)
to區(qū)
-
老年代major
永久代/元空間
java引用
強引用
軟引用
弱引用
虛引用
java如何標識對象是否存活?
引用計數(shù):循環(huán)計數(shù)問題
可達性分析:GCRoot
string對象存活:字符串無string對象引用
class存活:實例都釋放,且加載它的classloader被釋放
GC算法
-
標記清除
標記內(nèi)存回收對象,再回收
效率高,但存在內(nèi)存碎片問題
-
復(fù)制
- 將內(nèi)存劃分為兩塊,在其中一款分配對象,gc后,存活的對象移動到另外一塊
-
標記整理
- 類似復(fù)制,但沒有將內(nèi)存劃分為兩塊,只是在內(nèi)存的一端分配對象,存活的對象移動到另外一端
-
分代回收
-
新生代:復(fù)制算法
- 頻繁、快、高效
-
老年代:標記清除、標記整理
- 慢,低效,時間間隔久
-
-
常用gc處理器
串行
并發(fā)、并行:gc線程和用戶線程
設(shè)計模式
六大原則
-
單一原則
- 類、接口、方法功能單一
-
里氏代換原則
- 子類應(yīng)擴展父類抽象方法,但不應(yīng)該擴展父類非抽象方法
-
依賴倒置原則
- 抽象不依賴細節(jié),細節(jié)應(yīng)該依賴抽象
-
接口隔離原則
- 接口方法盡量少,功能劃分為多個接口
-
迪米特拉原則
- 類對依賴的類了解盡可能少,減少代碼耦合
-
開放封閉原則
- 擴展開放,修改封閉
集合/數(shù)據(jù)結(jié)構(gòu)
Collections
-
set
元素唯一
-
treeset
- 紅黑樹結(jié)構(gòu)
-
hashset
- hash算法計算元素存放地址
-
list
-
arraylist
- 線性存儲
-
vector
- 線程安全
-
linkedlist
可實現(xiàn)fifo、stack
同時實現(xiàn)了list和queue
-
-
queue
-
priorityqueue
- 優(yōu)先隊列
-
map
-
hashmap
數(shù)組+鏈表實現(xiàn)
多個數(shù)據(jù)的hash值相同時,通過鏈表連接
-
hashtable
- 線程安全
-
treemap
- 紅黑樹
-
linkedhashmap
基于雙向鏈表實現(xiàn)的hashmap
-
支持插入排序和訪問排序
- 訪問排序:每次訪問完后的元素放在鏈表頭,lru算法
其他
hash指元素使用hash算法計算存儲地址
link/tree,指元素是鏈表或者樹的結(jié)構(gòu)
IO
字節(jié)流
字符流
randomaccessfile
-
NIO
非阻塞、內(nèi)存映射方式,面向塊
channel
-
buffer
- 外部數(shù)據(jù)讀寫 與buffer交互
-
selector
- 異步IO
序列化
-
原理
-
java為序列化對象采用序列號機制,當序列化一個對象時,先檢查該對象是否已經(jīng)序列化
未序列化,采用流中數(shù)據(jù)構(gòu)建,并為該對象關(guān)聯(lián)一個序列號
已序列化,直接根據(jù)關(guān)聯(lián)的序列號,獲得該對象引用
-
-
版本號
根據(jù)類的超類、接口和域等屬性計算得到,當上述信息發(fā)生變化時,版本號會發(fā)生變化
一般將版本號靜態(tài)寫死,防止序列化時版本號不一致發(fā)生異常
static、transient域和方法,不能被序列化
序列化產(chǎn)生新的對象(不調(diào)用構(gòu)造函數(shù)),單例模式失效
創(chuàng)建對象的幾種方式
調(diào)用構(gòu)造函數(shù)
new
反射
-
序列化
- 不調(diào)用構(gòu)造函數(shù),通過二進制流構(gòu)造
-
clone
- 不調(diào)用構(gòu)造函數(shù)
其他
-
classloader
雙親委派模型
-
引導(dǎo)classloader
- 最頂層的加載類,主要加載核心類庫,%JRE_HOME%\lib下的rt.jar、resources.jar、charsets.jar和class等
-
擴展classloader
- 加載目錄%JRE_HOME%\lib\ext目錄下的jar包和class文件
-
系統(tǒng)(應(yīng)用)classloader
- 加載當前應(yīng)用classpath所有類
常用算法
- 深搜,DFS
Node* DFS(Node* root,int data)
{
if(root.key == data)
return root;
Node*[] chids = data->childs();
for(Node* child : childs)
{
if(child.key == data)
return child;
DFS(child,data);
}
}
- 廣搜
Node* BFS(Node* root,int data)
{
if(root.key == data)
return root;
FIFO<Node*> fifo = new FIFO<Node*>();
fifo.queue(root);
return BFS_Visit(fifo,data);
}
Node* BFS_Visit(FIFO<Node*> fifo,int data)
{
while(!fifo.isEmpty())
{
Node* item = fifo.dequeue();
if(item.key == data)
return item;
for(Node* child : item.childs())
fifo.queue(child);
}
}
-
排序
計數(shù)排序
快排
int quick_sort_part(int data[], int start, int end)
{
int i = start;
int j = start;
int key = end;
while (i <= end)
{
if (data[i] < data[key])
{
exchange(data[i], data[j]);
j++;
}
i++;
}
if (j != key)
{
exchange(data[j], data[key]);
}
return j;
}
//快速排序
void quick_sort(int data[], int start, int end)
{
if (start < end)
{
int k = quick_sort_part(data, start, end);
quick_sort(data, start, k - 1);
quick_sort(data, k + 1, end);
}
}
合并排序
-
堆排序
最大堆
最小堆
-
樹
二叉樹
二叉搜索樹
b樹
紅黑樹