Java多線程
文章目錄
一、程序 進程 線程
- 程序:指令集 靜態資源
- 進程:操作系統 調度程序 動態概念
- 線程:在進程內多條執行路徑
二、創建
Java中線程的創建常見有如三種基本形式
1、繼承Thread類,重寫該類的run方法
兔子類:
public class Rabbit extends Thread{
@Override
public void run() {
for (int i = 0; i <= 100; i++) {
System.out.println("兔子跑了"+i+"步");
}
}
}
烏龜類:
public class Tortoise extends Thread {
@Override
public void run() {
for (int i = 0; i <= 100; i++) {
System.out.println("烏龜跑了"+i+"步");
}
}
}
龜兔賽跑:
public class Game {
public static void main(String[] args) {
Rabbit rabbit = new Rabbit();
Tortoise tortoise = new Tortoise();
rabbit.start();
tortoise.start();
}
}
2、實現Runnable接口,並重寫該接口的run()方法。
該run()方法同樣是線程執行體。
步驟
- 創建Runnable實現類的實例。
- 並以此實例作爲Thread類的target來創建Thread對象,該Thread對象纔是真正的線程對象。
- Thread對象.start()
優勢
- 避免了單繼承的侷限性
- 便於共享資源 適合多個相同的程序代碼的線程去處理同一個資源
- 增加程序的健壯性,代碼可以被多個線程共享,代碼和數據獨立
- 線程池只能放入實現Runable或callable類線程,不能直接放入繼承Thread的類
public class Programmer implements Runnable {
@Override
public void run() {
for (int i = 0; i < 1000; i++) {
System.out.println("一遍寫代碼一遍看視頻");
}
}
}
Thread 這裏使用的事靜態代理模式
- 真實角色 我們這個Programmer類
- 代理角色 Thread 類
兩者都實現了run方法,代理角色要持有真實角色的引用 new Thread(programmer)
public class Start {
public static void main(String[] args) {
Programmer programmer = new Programmer();
Thread proxy = new Thread(programmer);
proxy.start();
for (int i = 0; i < 1000; i++) {
System.out.println("一遍聊QQ");
}
}
}
3、通過Callable接口實現多線程
步驟
- 創建Callable實現類+重寫call
- 藉助調度服務ExecutorService,獲得Future對象
ExecutorService ser= Executors.newFixedThreadPool(2);
Future result = ser.submit(實現類對象)
- 獲取值 result.get()
- 停止服務ser.shutdownNow()
優勢
- 與實行Runnable相比, Callable功能更強大些
- 可以有返回值, 支持泛型的返回值
- 可以拋出異常
線程池
Java通過Executors提供四種線程池,分別爲:
- newCachedThreadPool
創建一個可緩存線程池,如果線程池長度超過處理需要,可靈活回收空閒線程,若無可回收,則新建線程。 - newFixedThreadPool
創建一個定長線程池,可控制線程最大併發數,超出的線程會在隊列中等待。 - newScheduledThreadPool
創建一個定長線程池,支持定時及週期性任務執行。 - newSingleThreadExecutor
創建一個單線程化的線程池,它只會用唯一的工作線程來執行任務,保證所有任務按照指定順序(FIFO, LIFO, 優先級)執行。
具體實現
Future接口
- 可以對具體Runnable、 Callable任務的執行結果進行取消、 查詢是否完成、 獲取結果等。
public class Call {
public static void main(String[] args) throws ExecutionException, InterruptedException {
//創建一個可重用固定線程數的線程池
ExecutorService ser = Executors.newFixedThreadPool(2);
Race tortoise = new Race("千年王八",1000L);
Race rabbit = new Race("兔子",500L);
//FutureTask 也可以瞭解一下
Future<Integer> result = ser.submit(tortoise);
Future<Integer> result2 = ser.submit(rabbit);
Thread.sleep(2000);
tortoise.setFlag(false);
rabbit.setFlag(false);
int num1 = result.get();
int num2 = result2.get();
System.out.println("烏龜跑了————>"+num1);
System.out.println("兔子跑了————>"+num2);
ser.shutdownNow();
}
}
class Race implements Callable<Integer> {
private String name;//名稱
private long time;//延時時間
private boolean flag = true;
private int step = 0;
public Race() {
}
public Race(String name) {
this.name = name;
}
public Race(String name, long time) {
this.name = name;
this.time = time;
}
@Override
public Integer call() throws Exception {
while (flag){
Thread.sleep(time);
step++;
}
return step;
}
Getter Setter方法省略……
}
FutureTask實現類
- FutrueTask是Futrue接口的唯一的實現類
- FutureTask 同時實現了Runnable, Future接口。 它既可以作爲Runnable被線程執行, 又可以作爲Future得到Callable的返回值
//第一種方式
ExecutorService executor = Executors.newCachedThreadPool();
Task task = new Task();
FutureTask<Integer> futureTask = new FutureTask<Integer>(task);
executor.submit(futureTask);
executor.shutdown();
//第二種方式,注意這種方式和第一種方式效果是類似的,只不過一個使用的是ExecutorService,一個使用的是Thread
Task task = new Task();
FutureTask<Integer> futureTask = new FutureTask<Integer>(task);
Thread thread = new Thread(futureTask);
thread.start();
三、線程狀態
後續待學習整理