多線程基礎(chǔ)
基本概念
同步、異步
- 同步 線程B要等待線程A的執(zhí)行結(jié)果之后才能執(zhí)行。要順序執(zhí)行
image
- 異步 相對(duì)同步來說,彼此獨(dú)立,在在等待某事件的過程中可以繼續(xù)做自己的事。
image
這個(gè)更直觀 小人相當(dāng)于一個(gè)線程

image
并行/并發(fā)
并發(fā)和并行是異步的兩種實(shí)現(xiàn)形式,并行是真正的異步
-
并發(fā)
- 單核cpu情況,同一時(shí)刻只能分配給一個(gè)線程使用。其他等待。是邏輯層面的同時(shí)工作
image -
并行
-
多核cpu情況下,每個(gè)cpu都可以分配一個(gè)線程使用,同一時(shí)刻執(zhí)行多個(gè)并發(fā)任務(wù)。叫并行
image
-
線程狀態(tài)
image
線程狀態(tài)共5種:創(chuàng)建、就緒、運(yùn)行、==阻塞(阻塞、等待、鎖定)==、終止。
創(chuàng)建線程
- 繼承Thread類
public class A extends Thread {
//繼承Thread類 實(shí)現(xiàn)run接口
@Override
public void run() {
System.out.println("123");
}
public static void main(String[] args) {
A a = new A();//創(chuàng)建線程,線程進(jìn)入創(chuàng)建狀態(tài)
a.start();//開啟線程,線程進(jìn)入runnable
}
}
- 實(shí)現(xiàn)Runable接口
public class A implements Runnable {
//繼承Thread類 實(shí)現(xiàn)run接口
@Override
public void run() {
System.out.println("123");
}
public static void main(String[] args) {
//創(chuàng)建一個(gè)runable實(shí)現(xiàn)類的對(duì)象
A a = new A();
//不是啟動(dòng)線程,只是方法調(diào)用
a.run();
}
}
- 實(shí)現(xiàn)Callable接口
public class A implements Callable<Integer> {
//繼承Callable類 實(shí)現(xiàn)call接口 call具有返回值
@Override
public Integer call() throws Exception {
return 123;
}
public static void main(String[] args) throws ExecutionException, InterruptedException {
//創(chuàng)建一個(gè)callable實(shí)現(xiàn)類的對(duì)象
A a = new A();
//用FutureTask封裝
FutureTask<Integer> future = new FutureTask<Integer>(a);
//創(chuàng)建一個(gè)線程
Thread thread = new Thread(future);
thread.start();
//線程啟動(dòng)后可以獲取返回值
Integer integer = future.get();
System.out.println(integer);
}
}
實(shí)現(xiàn)Runnable接口比繼承Thread類優(yōu)勢(shì):
線程池只能放入實(shí)現(xiàn)Runable的
避免單繼承的限制
-
代碼被多個(gè)線程共享,代碼和數(shù)據(jù)獨(dú)立,例如
- 繼承Thread
int ticket=10;
MyThread t1=new MyThread();
t1.start();
MyThread t2=new MyThread();
t2.start();
//上面的代碼 就會(huì)賣出20張票 沒有數(shù)據(jù)共享
- 實(shí)現(xiàn)接口
int ticket=10;
MyThread t=new MyThread();
t.run();
t.run();
//這樣多個(gè)線程的時(shí)候,就能實(shí)現(xiàn)數(shù)據(jù)共享
線程其他狀態(tài)
-
Runnable(就緒)
線程調(diào)用start()方法,狀態(tài)為可執(zhí)行,等待獲取cpu的使用權(quán)
-
Running(運(yùn)行狀態(tài))
獲得cpu的使用權(quán),執(zhí)行代碼。
-
Blocked(阻塞)
-
等待阻塞
執(zhí)行wait()方法,jvm把該線程放入==等待池==
-
同步阻塞
獲取同步鎖時(shí),鎖被其他線程占用,jvm把該線程放入==鎖池==
-
其他阻塞
執(zhí)行sleep()或join()或發(fā)出i/o請(qǐng)求時(shí),jvm把線程設(shè)置為阻塞狀態(tài)
-
常用函數(shù)說明
-
sleep()
- 在指定的毫秒數(shù)內(nèi)讓線程休眠,進(jìn)入阻塞狀態(tài)。
-
join()
- 主線程需要等待調(diào)用join的子線程執(zhí)行完畢。
-
yield() 退讓
- 暫停當(dāng)前正在執(zhí)行的線程對(duì)象,并執(zhí)行其他線程,該線程進(jìn)入runnable狀態(tài)
- 目的:是為了讓相同優(yōu)先級(jí)的線程之間輪轉(zhuǎn)執(zhí)行。
- 實(shí)際情況下,并不一定有效果,因?yàn)樽尣街?,該線程還是有可能被再次選中
| sleep | yield |
|---|---|
| 在指定時(shí)間內(nèi)肯定不會(huì)被執(zhí)行 | 隨時(shí)都可以獲取鎖 |
| 允許較低優(yōu)先級(jí)的線程獲得運(yùn)行機(jī)會(huì) | 只能同級(jí) |
- interrupt() 中斷
- 并不能中斷線程!
- 只是給線程發(fā)出一個(gè)中斷信號(hào)。
- wait()休眠
- 線程在獲取對(duì)象鎖之后,釋放對(duì)象鎖,所以必須是再synchronized{}中
- notify() 喚醒
- 線程對(duì)對(duì)象鎖的喚醒操作。也要和synchronized{}搭配使用
- notify并不能馬上獲取對(duì)象鎖,而是再synchronized語句塊結(jié)束,自動(dòng)釋放鎖之后,jvm在所有的wait()的線程中隨機(jī)選取。
- 有個(gè)經(jīng)典的題,就是三個(gè)線程。分別打印10次A、B、C,要求順序執(zhí)行
image
public class MyThreadPrinter implements Runnable {
private String name;
private Object prev;
private Object self;
/**
*
* @param name 需要打印的東西
* @param prev 上一個(gè)對(duì)象
* @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) {
//需要獲取上一個(gè)對(duì)象的鎖和本身的鎖
synchronized (prev) {
synchronized (self) {
//重點(diǎn)在這
//打印本身的信息
System.out.print(name);
count--;
// 打印完 name了 可以喚醒了 因?yàn)樯弦粋€(gè)線程把本身給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中使用 | 任何地方 |