多線程技術講解
傳統線程技術的回顧
1. 多線程的創建(兩種)
- 通過創建線程對象,複寫run(),通過start()調用該線程。
本質是繼承
Thread thread = new Thread(){
@Override
public void run() {...}
};
thread.start();
- 通過有參構造創建線程對象,複寫run(),通過start()調用該線程。
本質是接口實現Runnable
Thread thread2 = new Thread(new Runnable() {
@Override
public void run() {...}
});
thread2.start();
- 源碼
- 首先進入構造方法中看見init方法
通過對成員變量的target的賦值,就能實現多線程。
2. 線程池的創立
傳統的線程創建,壽命只有從線程對象的確定,以及到線程代碼的結束,如果一個程序需要不同的時間,創建大量不同的線程,用上述的兩種方式創建線程,太過於消耗內存資源。由此引入新的的技術
- 線程池技術
線程池的創立都是通過Executors創建,通過代理實現不同類型線程池的創建,
線程池都是通過不同的隊列的實現,(LinkedBlockingQueue,SynchronousQueue)
// 固定線程數
ExecutorService threadPool = Executors.newFixedThreadPool(3);
// 根據任務調度線程數(首選,當它回收舊線程時,停止創建新線程,在使用出問題時,才使用newFixedThreadPool)
ExecutorService threadPool = Executors.newCachedThreadPool();
// 單線程池像是newFixedThreadPool(1);在處理單線程同步問題的時候,可以減少對同步複雜性,因爲只有一個線程執行,就不存在同步的可能
ExecutorService threadPool = Executors.newSingleThreadExecutor();
常用的API
- execute(Runnable command ) 線程調用線程池資源的核心
- Thread.CurrentThread()獲得當前線程對象
- shutdownNow()
- shutdown();
例子
ExecutorService threadPool = Executors.newSingleThreadExecutor();
for (int i = 0; i < 10; i++) {
final int task = i;
threadPool.execute(new Runnable() {
@Override
public void run() { }
}
});
}
System.out.println("shutdown");
threadPool.shutdownNow();
2.1 從任務中的返回值
- Runnable是執行工作的獨立任務,但是它不返回值,如希望在任務完成時能返回一個值,那麼可以實現Callable接口而不是Runnable接口。Callable是一種具有類型參數的泛型,它的類型參數表示的是從call()而不是run()中返回的值,並且使用ExecutorService.submit()方法調用它
class TaskWithResult implements Callable<String>{
private int id;
public TaskWithResult(int id){
this.id = id;
}
public String call(){
return "result of TaskWithResult"+id;
}
}
public class CallableDemo{
public static void main(String[] args){
ExecutorService exec = Executors.newCacheThreadPool();
List<Future<String>> result = new ArrayList<>();
for(int i = 0;i<10;i++){
result.add(exec.submit(new TaskWithResult(1)));
}
for(Future<String> fs : result){
try{
System.out.println(fs.get());
}catch(Exception e){
e.printStackTrace();
}finally{
exec.shutdown();
}
}
}
}
- submit()方法會產生Future對象,它用Callable返回結果的特定類型進行了參數化,可以用isDone()查詢Future是否完成,若完成,那麼就有一個結果,通過get()獲取,若未完成,直接用get()方法獲取,則會阻塞,直至結果就緒。 一般先用isDone()判斷是否完成,再用get