一导街、CPU時間片
CPU時間片即CPU分配給每個線程的執(zhí)行時間段涝滴,稱作它的時間片恶座。
二便脊、什么是上下文切換
CPU通過給每個線程分配CPU時間片來實現這個機制蚂四。時間片是CPU分配給各個線程的時間,因為時間片非常短,所以CPU通過不停地切換線程執(zhí)行遂赠,讓我們感覺多個線程時同時執(zhí)行的久妆,時間片一般是幾十毫秒(ms)。
CPU通過時間片分配算法來循環(huán)執(zhí)行任務跷睦,當前任務執(zhí)行一個時間片后會切換到下一個任務筷弦。但是,在切換前會保存上一個任務的狀態(tài)送讲,以便下次切換回這個任務時奸笤,可以再次加載這個任務的狀態(tài),從任務保存到再加載的過程就是一次上下文切換哼鬓。
- 若當前線程還在運行而時間片結束后监右,CPU將被剝奪并分配給另一個線程。
- 若線程在時間片結束前阻塞或結束异希,CPU進行線程切換健盒。而不會造成CPU資源浪費。
三称簿、上下文切換造成的影響
我們可以通過對比串聯(lián)執(zhí)行和并發(fā)執(zhí)行進行對比扣癣。
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)次數,對比串行運行和并發(fā)運行的時間測試結果:
通過數據的對比我們可以看出憨降。在一萬以下的循環(huán)次數時父虑,串聯(lián)的執(zhí)行速度比并發(fā)的執(zhí)行速度塊。是因為線程上下文切換導致額外的開銷授药。
在Linux系統(tǒng)下可以使用vmstat命令來查看上下文切換的次數士嚎,如果要查看上下文切換的時長,可以利用Lmbench3悔叽,這是一個性能分析工具莱衩。
四、如何減少上下文切換導致額外的開銷
減少上下文切換次數便可以提高多線程的運行效率娇澎。減少上下文切換的方法有無鎖并發(fā)編程笨蚁、CAS算法、避免創(chuàng)建過多的線程和使用協(xié)程趟庄。
無鎖并發(fā)編程.當任何特定的運算被阻塞的時候括细,所有CPU可以繼續(xù)處理其他的運算。換種方式說戚啥,在無鎖系統(tǒng)中勒极,當給定線程被其他線程阻塞的時候,所有CPU可以不停的繼續(xù)處理其他工作虑鼎。無鎖算法大大增加系統(tǒng)整體的吞吐量,因為它只偶爾會增加一定的交易延遲。大部分高端數據庫系統(tǒng)是基于無鎖算法而構造的炫彩,以滿足不同級別匾七。
CAS算法。Java提供了一套原子性操作的數據類型(java.util.concurrent.atomic包下)江兢,使用CAS算法來更新數據昨忆,不需要加鎖。如:AtomicInteger杉允、AtomicLong等邑贴。
避免創(chuàng)建過多的線程。如任務量少時叔磷,盡可能減少創(chuàng)建線程拢驾。對于某個時間段任務量很大的這種情況,我們可以通過線程池來管理線程的數量改基,避免創(chuàng)建過多線程繁疤。
協(xié)程:即協(xié)作式程序,其思想是秕狰,一系列互相依賴的協(xié)程間依次使用CPU稠腊,每次只有一個協(xié)程工作,而其他協(xié)程處于休眠狀態(tài)鸣哀。如:JAVA中使用wait和notify來達到線程之間的協(xié)同工作架忌。