1.進(jìn)程和線程
進(jìn)程:由指令和數(shù)據(jù)組成方妖,指令加載到CPU匣缘,數(shù)據(jù)加載到內(nèi)存窘哈,那么進(jìn)程啟動澄成。
進(jìn)程栗子:瀏覽器胧洒,記事本...可以同時開多個 那么就是多進(jìn)程
線程:一個線程就是一個指令流,線程將指令按順序交給CPU
線程栗子:在記事本(進(jìn)程)中保存墨状,線程將保存的代碼交給CPU卫漫,CPU執(zhí)行IO操作。
注:java中線程作為最小調(diào)度單位肾砂,進(jìn)程作為資源分配的最小單位
注2:windows中進(jìn)程是不活動的列赎,只是線程的容器
單核心CPU:
多核心CPU:
并發(fā)
Rob Pike(golang語言創(chuàng)始者):
并發(fā)(concurrent)是同一時間應(yīng)對(dealing with)多件事情的能力。
并行(parallel)是同一時間動手做(doing)多件事情的能力镐确。
并發(fā):多線程問題,微觀上單核心還是逐個執(zhí)行每一個小指令
并行:栗子--java stream并行流 同時調(diào)度CPU所有核心來處理數(shù)據(jù)
2.為什么多線程可以提高效率包吝?
2.1 同步和異步
1.同步
一般我們方法執(zhí)行都是主線程在執(zhí)行,方法會從上到下依次運(yùn)行源葫,上面的代碼沒有運(yùn)行完后面的代碼是不能運(yùn)行的(同步的概念)
2.異步
多線程情況下诗越,那么可以讓新的線程去處理較為耗時的任務(wù),主線程繼續(xù)運(yùn)行(異步的概念)臼氨。
栗子:用戶點(diǎn)擊執(zhí)行某個復(fù)雜任務(wù)掺喻,如果不是多線程,那么必須等待執(zhí)行任務(wù)的代碼結(jié)束 才能告知用戶這個指令執(zhí)行完成。多線程可以讓新線程去處理復(fù)雜的任務(wù)感耙,主線程繼續(xù)跑褂乍,跑完主線程告訴返回給用戶,剛剛那個指令已經(jīng)開啟了即硼。(異步)
3.線程創(chuàng)建方法
3.1 new Thread
//new thread
Thread newThread = new Thread("new_thread"){
@Override
public void run() {
log.info("print new thread and override run");
}
};
newThread.start();
//new thread by lambda
Thread newThreadLambda = new Thread(() -> log.info("print new thread override run by lambda"));
newThreadLambda.setName("new_thread_lambda");
newThreadLambda.start();
3.2 Runnable
//特點(diǎn):任務(wù)和線程分開 寫起來方便一些 畢竟實(shí)際中執(zhí)行都是使用線程池
//new runnable
Runnable runnable = new Runnable() {
@Override
public void run() {
log.info("print new runnable thread and override run");
}
};
Thread runnableThread = new Thread(runnable);
runnableThread.start();
//new runnable by lambda
Runnable runnableLambda = () -> log.info("print new runnable thread and override run by lambda");
Thread runnableLambdaThread = new Thread(runnableLambda);
runnableLambdaThread.start();
3.3 Callable
//特點(diǎn):繼承了runnable(配合線程) 和 future(能夠返回值逃片,拋出異常)
//futureTask
FutureTask<Object> futureTask = new FutureTask<>(new Callable<Object>() {
@Override
public String call() throws Exception {
log.info("print new future task thread and override call");
Thread.sleep(3000);
return "FutureTask return";
}
});
new Thread(futureTask, "futureTask_thread").start();
//注意:main線程中 get方法要等call方法執(zhí)行完 才能拿到返回值 才會執(zhí)行
Object result = futureTask.get();
log.warn("result = {}", result.toString());
// futureTask by lambda
FutureTask<Object> futureTaskLambda = new FutureTask<>(() -> {
log.info("print new future task thread and override call by lambda");
Thread.sleep(5000);
return "FutureTask lambda return";
});
new Thread(futureTaskLambda, "futureTask_threadLambda").start();
Object resultLambda = futureTaskLambda.get();
log.warn("resultLambda = {}", resultLambda.toString());
4. 線程運(yùn)行原理
4.1 棧
棧:包含:局部變量表,操作數(shù)棧只酥,動態(tài)鏈接褥实,方法返回值和一些附加信息
ps:在debug的時候我們看到的哪些屬性 都是idea從棧幀里面獲取的
線程啟動--虛擬機(jī)為線程開辟一塊棧內(nèi)存
線程的每個方法運(yùn)行在一個棧幀之上
4.2 線程的上下文切換 Context Switch
概念:因?yàn)橐恍┰驅(qū)е耤pu不在執(zhí)行當(dāng)前線程了,轉(zhuǎn)而執(zhí)行另一個線程代碼
4.2.1 線程的cpu時間片用完
4.2.2 垃圾回收
因?yàn)槔厥諘屗芯€程停下來裂允,
所有我們需要對jvm調(diào)優(yōu)损离,來減少垃圾回收的次數(shù)
4.2.3 有更高優(yōu)先級的線程需要運(yùn)行
4.2.4 線程自己調(diào)用了sleep,yield绝编,wait等方法
注意:上下文切換會操作系統(tǒng)幫你保存線程狀態(tài)僻澎,包括:讓程序計數(shù)器將指令地址記住(運(yùn)行到哪一行了)十饥,局部變量窟勃,返回地址等 頻繁的上下文切換 會
5. 線程安全問題
關(guān)鍵點(diǎn):多個線程處理共享資源
關(guān)鍵點(diǎn)2:沒有開多線程,每個方法有自己的線程棧逗堵,也就沒線程安全問題(常規(guī)情況下)
感謝:
《Java并發(fā)編程的藝術(shù)》方騰飛秉氧,魏鵬,程曉明
《Java并發(fā)實(shí)現(xiàn)原理:JDK源碼剖析》余春龍
全面深入學(xué)習(xí)java并發(fā)編程蜒秤,java基礎(chǔ)進(jìn)階中級必會教程_嗶哩嗶哩 (゜-゜)つロ 干杯~-bilibili