多線程技術概述
線程與進程
進程
是指一個內存中運行的應用程序,每個進程都有一個獨立的內存空間
線程
- 是進程中的一個執(zhí)行路徑,共享一個內存空間,線程之間可以自由切換,并發(fā)執(zhí)行. 一個進程最少有一個線程
- 線程實際上是在進程基礎之上的進一步劃分,一個進程啟動之后,里面的若干執(zhí)行路徑又可以劃分成若干個線程
線程調度
分時調度
所有線程輪流使用 CPU 的使用權,平均分配每個線程占用 CPU 的時間。
搶占式調度
- 優(yōu)先讓優(yōu)先級高的線程使用 CPU,如果線程的優(yōu)先級相同,那么會隨機選擇一個(線程隨機性)
Java使用的為搶占式調度 - CPU使用搶占式調度模式在多個線程間進行著高速的切換。對于CPU的一個核新而言,某個時刻,只能執(zhí)行一個線程,而 CPU的在多個線程間切換速度相對我們的感覺要快,看上去就是 在同一時刻運行。 其實多線程程序并不能提高程序的運行速度,但能夠提高程序運行效率,讓CPU的 使用率更高。
同步與異步
- 同步:排隊執(zhí)行 , 效率低但是安全.
- 異步:同時執(zhí)行 , 效率高但是數據不安全.
并發(fā)與并行
- 并發(fā):指兩個或多個事件在同一個時間段內發(fā)生。
- 并行:指兩個或多個事件在同一時刻發(fā)生(同時發(fā)生)
線程池 Executors
如果并發(fā)的線程數量很多,并且每個線程都是執(zhí)行一個時間很短的任務就結束了,這樣頻繁創(chuàng)建線程就會大大降低 系統(tǒng)的效率,因為頻繁創(chuàng)建線程和銷毀線程需要時間. 線程池就是一個容納多個線程的容器,池中的線程可以反復使用,省去了頻繁創(chuàng)建線程對象的操作,節(jié)省了大量的時間和資源。
線程池的好處
- 降低資源消耗。
- 提高響應速度。
- 提高線程的可管理性。
Java中的四種線程池 . ExecutorService
1. 緩存線程池
/**
* 緩存線程池.
* (長度無限制)
* 執(zhí)行流程:
* 1. 判斷線程池是否存在空閑線程
* 2. 存在則使用
* 3. 不存在,則創(chuàng)建線程 并放入線程池, 然后使用
*/
ExecutorService service = Executors.newCachedThreadPool();
//向線程池中 加入 新的任務
service.execute(new Runnable() {
@Override
public void run() {
System.out.println("線程的名稱:"+Thread.currentThread().getName());
}
});
service.execute(new Runnable() {
@Override
public void run() {
System.out.println("線程的名稱:"+Thread.currentThread().getName());
}
});
service.execute(new Runnable() {
@Override
public void run() {
System.out.println("線程的名稱:"+Thread.currentThread().getName());
}
});
2. 定長線程池
/**
* 定長線程池.
* (長度是指定的數值)
* 執(zhí)行流程:
* 1. 判斷線程池是否存在空閑線程
* 2. 存在則使用
* 3. 不存在空閑線程,且線程池未滿的情況下,則創(chuàng)建線程 并放入線程池, 然后使用
* 4. 不存在空閑線程,且線程池已滿的情況下,則等待線程池存在空閑線程
*/
ExecutorService service = Executors.newFixedThreadPool(2);
service.execute(new Runnable() {
@Override
public void run() {
System.out.println("線程的名稱:"+Thread.currentThread().getName());
}
});
service.execute(new Runnable() {
@Override
public void run() {
System.out.println("線程的名稱:"+Thread.currentThread().getName());
}
});
3. 單線程線程池
效果與定長線程池 創(chuàng)建時傳入數值1 效果一致.
/**
* 單線程線程池.
* 執(zhí)行流程:
* 1. 判斷線程池 的那個線程 是否空閑
* 2. 空閑則使用
* 4. 不空閑,則等待 池中的單個線程空閑后 使用
*/
ExecutorService service = Executors.newSingleThreadExecutor();
service.execute(new Runnable() {
@Override
public void run() {
System.out.println("線程的名稱:"+Thread.currentThread().getName());
}
});
service.execute(new Runnable() {
@Override
public void run() {
System.out.println("線程的名稱:"+Thread.currentThread().getName());
}
});
4. 周期性任務定長線程池
public static void main(String[] args) {
/**
* 周期任務 定長線程池.
* 執(zhí)行流程:
* 1. 判斷線程池是否存在空閑線程
* 2. 存在則使用
* 3. 不存在空閑線程,且線程池未滿的情況下,則創(chuàng)建線程 并放入線程池, 然后使用
* 4. 不存在空閑線程,且線程池已滿的情況下,則等待線程池存在空閑線程
*
* 周期性任務執(zhí)行時:
* 定時執(zhí)行, 當某個時機觸發(fā)時, 自動執(zhí)行某任務 .*/
ScheduledExecutorService service = Executors.newScheduledThreadPool(2);
/**
* 定時執(zhí)行
* 參數1. runnable類型的任務
* 參數2. 時長數字
* 參數3. 時長數字的單位
*/
/*service.schedule(new Runnable() {
@Override
public void run() {
System.out.println("倆人相視一笑~ 嘿嘿嘿");
}
},5,TimeUnit.SECONDS);
*/
/**
* 周期執(zhí)行
* 參數1. runnable類型的任務
* 參數2. 時長數字(延遲執(zhí)行的時長)
* 參數3. 周期時長(每次執(zhí)行的間隔時間)
* 參數4. 時長數字的單位
*/
service.scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
System.out.println("倆人相視一笑~ 嘿嘿嘿");
}
},5,2,TimeUnit.SECONDS);
}
Runnable 與 Callable
接口定義
//Callable接口
public interface Callable<V> {
V call() throws Exception;
}
//Runnable接口
public interface Runnable {
public abstract void run();
}
Callable使用步驟
- 編寫類實現(xiàn)Callable接口 , 實現(xiàn)call方法
class XXX implements Callable<T> {
@Override
public <T> call() throws Exception {
return T;
}
}
- 創(chuàng)建FutureTask對象 , 并傳入第一步編寫的Callable類對象
FutureTask<Integer> future = new FutureTask<>(callable);- 通過Thread,啟動線程
new Thread(future).start();
Runnable 與 Callable的相同點
- 都是接口
- 都可以編寫多線程程序
- 都采用Thread.start()啟動線程
Runnable 與 Callable的不同點
- Runnable沒有返回值;Callable可以返回執(zhí)行結果
- Callable接口的call()允許拋出異常;Runnable的run()不能拋出
Callable獲取返回值
Callalble接口支持返回執(zhí)行結果,需要調用FutureTask.get()得到,此方法會阻塞主進程的繼續(xù)往下執(zhí)行,如果不調用不會阻塞。