多線程基礎
基本概念
同步弥虐、異步
- 同步 線程B要等待線程A的執(zhí)行結果之后才能執(zhí)行。要順序執(zhí)行
image
- 異步 相對同步來說媚赖,彼此獨立霜瘪,在在等待某事件的過程中可以繼續(xù)做自己的事。
image
這個更直觀 小人相當于一個線程
image
并行/并發(fā)
并發(fā)和并行是異步的兩種實現(xiàn)形式惧磺,并行是真正的異步
-
并發(fā)
- 單核cpu情況颖对,同一時刻只能分配給一個線程使用。其他等待磨隘。是邏輯層面的同時工作
image -
并行
-
多核cpu情況下缤底,每個cpu都可以分配一個線程使用,同一時刻執(zhí)行多個并發(fā)任務番捂。叫并行
image
-
線程狀態(tài)
image
線程狀態(tài)共5種:創(chuàng)建个唧、就緒、運行设预、==阻塞(阻塞徙歼、等待、鎖定)==、終止魄梯。
創(chuàng)建線程
- 繼承Thread類
public class A extends Thread {
//繼承Thread類 實現(xiàn)run接口
@Override
public void run() {
System.out.println("123");
}
public static void main(String[] args) {
A a = new A();//創(chuàng)建線程桨螺,線程進入創(chuàng)建狀態(tài)
a.start();//開啟線程,線程進入runnable
}
}
- 實現(xiàn)Runable接口
public class A implements Runnable {
//繼承Thread類 實現(xiàn)run接口
@Override
public void run() {
System.out.println("123");
}
public static void main(String[] args) {
//創(chuàng)建一個runable實現(xiàn)類的對象
A a = new A();
//不是啟動線程酿秸,只是方法調用
a.run();
}
}
- 實現(xiàn)Callable接口
public class A implements Callable<Integer> {
//繼承Callable類 實現(xiàn)call接口 call具有返回值
@Override
public Integer call() throws Exception {
return 123;
}
public static void main(String[] args) throws ExecutionException, InterruptedException {
//創(chuàng)建一個callable實現(xiàn)類的對象
A a = new A();
//用FutureTask封裝
FutureTask<Integer> future = new FutureTask<Integer>(a);
//創(chuàng)建一個線程
Thread thread = new Thread(future);
thread.start();
//線程啟動后可以獲取返回值
Integer integer = future.get();
System.out.println(integer);
}
}
實現(xiàn)Runnable接口比繼承Thread類優(yōu)勢:
線程池只能放入實現(xiàn)Runable的
避免單繼承的限制
-
代碼被多個線程共享灭翔,代碼和數(shù)據(jù)獨立,例如
- 繼承Thread
int ticket=10;
MyThread t1=new MyThread();
t1.start();
MyThread t2=new MyThread();
t2.start();
//上面的代碼 就會賣出20張票 沒有數(shù)據(jù)共享
- 實現(xiàn)接口
int ticket=10;
MyThread t=new MyThread();
t.run();
t.run();
//這樣多個線程的時候辣苏,就能實現(xiàn)數(shù)據(jù)共享
線程其他狀態(tài)
-
Runnable(就緒)
線程調用start()方法肝箱,狀態(tài)為可執(zhí)行,等待獲取cpu的使用權
-
Running(運行狀態(tài))
獲得cpu的使用權稀蟋,執(zhí)行代碼狭园。
-
Blocked(阻塞)
-
等待阻塞
執(zhí)行wait()方法,jvm把該線程放入==等待池==
-
同步阻塞
獲取同步鎖時糊治,鎖被其他線程占用唱矛,jvm把該線程放入==鎖池==
-
其他阻塞
執(zhí)行sleep()或join()或發(fā)出i/o請求時,jvm把線程設置為阻塞狀態(tài)
-
常用函數(shù)說明
-
sleep()
- 在指定的毫秒數(shù)內讓線程休眠井辜,進入阻塞狀態(tài)绎谦。
-
join()
- 主線程需要等待調用join的子線程執(zhí)行完畢。
-
yield() 退讓
- 暫停當前正在執(zhí)行的線程對象粥脚,并執(zhí)行其他線程窃肠,該線程進入runnable狀態(tài)
- 目的:是為了讓相同優(yōu)先級的線程之間輪轉執(zhí)行。
- 實際情況下刷允,并不一定有效果冤留,因為讓步之后,該線程還是有可能被再次選中
sleep | yield |
---|---|
在指定時間內肯定不會被執(zhí)行 | 隨時都可以獲取鎖 |
允許較低優(yōu)先級的線程獲得運行機會 | 只能同級 |
- interrupt() 中斷
- 并不能中斷線程树灶!
- 只是給線程發(fā)出一個中斷信號纤怒。
- wait()休眠
- 線程在獲取對象鎖之后,釋放對象鎖天通,所以必須是再synchronized{}中
- notify() 喚醒
- 線程對對象鎖的喚醒操作泊窘。也要和synchronized{}搭配使用
- notify并不能馬上獲取對象鎖,而是再synchronized語句塊結束像寒,自動釋放鎖之后烘豹,jvm在所有的wait()的線程中隨機選取。
- 有個經典的題诺祸,就是三個線程携悯。分別打印10次A、B筷笨、C,要求順序執(zhí)行
image
public class MyThreadPrinter implements Runnable {
private String name;
private Object prev;
private Object self;
/**
*
* @param name 需要打印的東西
* @param prev 上一個對象
* @param self 自己
*/
private MyThreadPrinter(String name, Object prev, Object self) {
this.name = name;
this.prev = prev;
this.self = self;
}
@Override
public void run() {
int count = 10;
while (count > 0) {
//需要獲取上一個對象的鎖和本身的鎖
synchronized (prev) {
synchronized (self) {
//重點在這
//打印本身的信息
System.out.print(name);
count--;
// 打印完 name了 可以喚醒了 因為上一個線程把本身給wait了
self.notify();
}
try {
// 釋放鎖 等待notify才可以重新獲取鎖
//比如a執(zhí)行完 需要執(zhí)行b 需要 b a鎖 就把c給wait
prev.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public static void main(String[] args) throws Exception {
Object a = new Object();
Object b = new Object();
Object c = new Object();
MyThreadPrinter pa = new MyThreadPrinter("A", c, a);
//pa執(zhí)行完 c就wait了 只有a b可以獲取鎖
MyThreadPrinter pb = new MyThreadPrinter("B", a, b);
MyThreadPrinter pc = new MyThreadPrinter("C", b, c);
new Thread(pa).start();
Thread.sleep(100); //確保按順序A憔鬼、B龟劲、C執(zhí)行
new Thread(pb).start();
Thread.sleep(100);
new Thread(pc).start();
Thread.sleep(100);
}
}
- wait / sleep
wait | sleep |
---|---|
釋放鎖 | 沒有釋放鎖 |
只能在同步快sync中使用 | 任何地方 |