一、CPU時間片
- CPU時間片即CPU分配給每個線程的執(zhí)行時間段,稱作它的時間片惹盼。CPU時間片一般為幾十毫秒(ms)。
二惫确、什么是上下文切換
CPU通過時間片段的算法來循環(huán)執(zhí)行線程任務(wù)手报,而循環(huán)執(zhí)行即每個線程允許運(yùn)行的時間后的切換,而這種循環(huán)的切換使各個程序從表面上看是同時進(jìn)行的改化。而切換時會保存之前的線程任務(wù)狀態(tài)掩蛤,當(dāng)切換到該線程任務(wù)的時候,會重新加載該線程的任務(wù)狀態(tài)陈肛。而這個從保存到加載的過程稱之為上下文切換揍鸟。
- 若當(dāng)前線程還在運(yùn)行而時間片結(jié)束后,CPU將被剝奪并分配給另一個線程句旱。
- 若線程在時間片結(jié)束前阻塞或結(jié)束阳藻,CPU進(jìn)行線程切換。而不會造成CPU資源浪費(fèi)谈撒。
三腥泥、上下文切換造成的影響
我們可以通過對比串聯(lián)執(zhí)行和并發(fā)執(zhí)行進(jìn)行對比。
private static final long count = 1000000;
public static void main(String[] args) throws Exception {
concurrency();
series();
}
/**
* 并發(fā)執(zhí)行
* @throws Exception
*/
private static void concurrency() throws Exception {
long start = System.currentTimeMillis();
//創(chuàng)建線程執(zhí)行a+=
Thread thread = new Thread(new Runnable() {
public void run() {
int a = 0;
for (int i = 0; i < count; i++) {
a += 1;
}
}
});
//啟動線程執(zhí)行
thread.start();
//使用主線程執(zhí)行b--;
int b = 0;
for (long i = 0; i < count; i++) {
b--;
}
//合并線程啃匿,統(tǒng)計時間
thread.join();
long time = System.currentTimeMillis() - start;
System.out.println("Concurrency:" + time + "ms, b = " + b);
}
/**
* 串聯(lián)執(zhí)行
*/
private static void series() {
long start = System.currentTimeMillis();
int a = 0;
for (long i = 0; i < count; i++) {
a += 1;
}
int b = 0;
for (int i = 0; i < count; i++) {
b--;
}
long time = System.currentTimeMillis() - start;
System.out.println("Serial:" + time + "ms, b = " + b + ", a = " + a);
}
通過修改循環(huán)次數(shù),對比串行運(yùn)行和并發(fā)運(yùn)行的時間測試結(jié)果:
循環(huán)次數(shù) | 并發(fā)執(zhí)行時間 | 串聯(lián)執(zhí)行時間 |
---|---|---|
一百萬 | 2ms | 4ms |
十萬 | 2ms | 2ms |
一萬 | 1ms | 0ms |
通過數(shù)據(jù)的對比我們可以看出蛔外。在一萬以下的循環(huán)次數(shù)時蛆楞,串聯(lián)的執(zhí)行速度比并發(fā)的執(zhí)行速度塊。是因為線程上下文切換導(dǎo)致額外的開銷夹厌。
在Linux系統(tǒng)下可以使用vmstat命令來查看上下文切換的次數(shù)豹爹,如果要查看上下文切換的時長,可以利用Lmbench3尊流,這是一個性能分析工具帅戒。
四、如何減少上下文切換導(dǎo)致額外的開銷
減少上下文切換次數(shù)便可以提高多線程的運(yùn)行效率崖技。減少上下文切換的方法有無鎖并發(fā)編程、CAS算法钟哥、避免創(chuàng)建過多的線程和使用協(xié)程迎献。
無鎖并發(fā)編程.當(dāng)任何特定的運(yùn)算被阻塞的時候,所有CPU可以繼續(xù)處理其他的運(yùn)算腻贰。換種方式說吁恍,在無鎖系統(tǒng)中,當(dāng)給定線程被其他線程阻塞的時候播演,所有CPU可以不停的繼續(xù)處理其他工作冀瓦。無鎖算法大大增加系統(tǒng)整體的吞吐量,因為它只偶爾會增加一定的交易延遲写烤。大部分高端數(shù)據(jù)庫系統(tǒng)是基于無鎖算法而構(gòu)造的翼闽,以滿足不同級別。
CAS算法洲炊。Java提供了一套原子性操作的數(shù)據(jù)類型(java.util.concurrent.atomic包下)感局,使用CAS算法來更新數(shù)據(jù),不需要加鎖暂衡。如:AtomicInteger询微、AtomicLong等。
避免創(chuàng)建過多的線程狂巢。如任務(wù)量少時撑毛,盡可能減少創(chuàng)建線程。對于某個時間段任務(wù)量很大的這種情況唧领,我們可以通過線程池來管理線程的數(shù)量藻雌,避免創(chuàng)建過多線程。
協(xié)程:即協(xié)作式程序疹吃,其思想是蹦疑,一系列互相依賴的協(xié)程間依次使用CPU,每次只有一個協(xié)程工作萨驶,而其他協(xié)程處于休眠狀態(tài)歉摧。如:JAVA中使用wait和notify來達(dá)到線程之間的協(xié)同工作。
參考:
《Java并發(fā)編程的藝術(shù)》