JUC總結(jié)
線程與進(jìn)程
進(jìn)程:是程序運(yùn)行和資源分配的基本單位,一個(gè)程序至少有一個(gè)進(jìn)程唉擂,一個(gè)進(jìn)程至少有一個(gè)線程想帅。進(jìn)程在執(zhí)行過(guò)程中擁有獨(dú)立的內(nèi)存單元,而多個(gè)線程共享內(nèi)存資源尿庐,減少切換次數(shù)忠怖,從而效率更高。
線程:是進(jìn)程的一個(gè)實(shí)體抄瑟,是 cpu 調(diào)度和分派的基本單位凡泣,是比程序更小的能獨(dú)立運(yùn)行的基本單位。同一進(jìn)程中的多個(gè)線程之間可以并發(fā)執(zhí)行锐借。
java如何開(kāi)啟線程问麸?
通過(guò)底層調(diào)用C語(yǔ)言,所以java本身無(wú)法創(chuàng)建線程钞翔!
public synchronized void start() {
/**
* This method is not invoked for the main method thread or "system"
* group threads created/set up by the VM. Any new functionality added
* to this method in the future may have to also be added to the VM.
*
* A zero status value corresponds to state "NEW".
*/
if (threadStatus != 0)
throw new IllegalThreadStateException();
/* Notify the group that this thread is about to be started
* so that it can be added to the group's list of threads
* and the group's unstarted count can be decremented. */
group.add(this);
boolean started = false;
try {
start0();
started = true;
} finally {
try {
if (!started) {
group.threadStartFailed(this);
}
} catch (Throwable ignore) {
/* do nothing. If start0 threw a Throwable then
it will be passed up the call stack */
}
}
}
// 本地方法严卖,底層的C++ ,Java 無(wú)法直接操作硬件
private native void start0();
并發(fā)與并行
并發(fā)(多線程操作同一個(gè)資源)
- CPU一核 布轿,模擬出來(lái)多條線程哮笆,天下武功,唯快不破汰扭,快速交替
并行(多個(gè)人一起行走)
- CPU多核 稠肘,多個(gè)線程可以同時(shí)執(zhí)行; 線程池
并行是在不同實(shí)體上的多個(gè)事件,并發(fā)是在同一實(shí)體上的多個(gè)事件
線程的幾種狀態(tài)
public enum State {
// 新生
NEW,
// 運(yùn)行
RUNNABLE,
// 阻塞
BLOCKED,
// 等待萝毛,死死地等
WAITING,
// 超時(shí)等待
TIMED_WAITING,
// 終止
TERMINATED;
}
wait/sleep 區(qū)別
-
sleep() 方法正在執(zhí)行的線程主動(dòng)讓出 cpu(然后 cpu 就可以去執(zhí)行其他任務(wù))项阴,在 sleep 指定時(shí)間后 cpu 再回到該線程繼續(xù)往下執(zhí)行(注意:sleep 方法只讓出了 cpu,而并不會(huì)釋放同步資源鎖)笆包;
而 wait() 方法則是指當(dāng)前線程讓自己暫時(shí)退讓出同步資源鎖环揽,以便其他正在等待該資源的線程得到該資源進(jìn)而運(yùn)行略荡,只有調(diào)用了 notify() 方法,之前調(diào)用 wait() 的線程才會(huì)解除 wait 狀態(tài)歉胶,可以去參與競(jìng)爭(zhēng)同步資源鎖汛兜,進(jìn)而得到執(zhí)行。(注意:notify 的作用相當(dāng)于叫醒睡著的人通今,而并不會(huì)給他分配任務(wù)粥谬,就是說(shuō) notify 只是讓之前調(diào)用 wait 的線程有權(quán)利重新參與線程的調(diào)度);
sleep() 方法可以在任何地方使用辫塌,而 wait() 方法則只能在同步方法或同步塊中使用漏策;
-
sleep() 是線程類(Thread)的方法,調(diào)用會(huì)暫停此線程指定的時(shí)間璃氢,但監(jiān)控依然保持哟玷,不會(huì)釋放對(duì)象鎖,到時(shí)間自動(dòng)恢復(fù)一也;wait() 是 Object 的方法巢寡,調(diào)用會(huì)放棄對(duì)象鎖,進(jìn)入等待隊(duì)列椰苟,待調(diào)用 notify()/notifyAll() 喚醒指定的線程或者所有線程抑月,才會(huì)進(jìn)入鎖池,不再次獲得對(duì)象鎖才會(huì)進(jìn)入運(yùn)行狀態(tài)舆蝴。
wait 會(huì)釋放鎖谦絮,sleep 睡覺(jué)了,抱著鎖睡覺(jué)洁仗,不會(huì)釋放!
Lock鎖
傳統(tǒng)Synchronized
修飾實(shí)例方法:作用于當(dāng)前對(duì)象實(shí)例加鎖层皱,進(jìn)入同步代碼前要獲得當(dāng)前對(duì)象實(shí)例的鎖;
-
修飾靜態(tài)方法:作用于當(dāng)前類對(duì)象加鎖赠潦,進(jìn)入同步代碼前要獲得當(dāng)前類對(duì)象的鎖 叫胖。也就是給當(dāng)前類加鎖,會(huì)作用于類的所有對(duì)象實(shí)例她奥,因?yàn)殪o態(tài)成員不屬于任何一個(gè)實(shí)例對(duì)象瓮增,是類成員(static 表明這是該類的一個(gè)靜態(tài)資源,不管 new了多少個(gè)對(duì)象哩俭,只有一份绷跑,所以對(duì)該類的所有對(duì)象都加了鎖)。
所以如果一個(gè)線程 A 調(diào)用一個(gè)實(shí)例對(duì)象的非靜態(tài) synchronized 方法凡资,而線程 B 需要調(diào)用這個(gè)實(shí)例對(duì)象所屬類的靜態(tài) synchronized 方法砸捏,是允許的,不會(huì)發(fā)生互斥現(xiàn)象,因?yàn)樵L問(wèn)靜態(tài) synchronized 方法占用的鎖是當(dāng)前類的鎖带膜,而訪問(wèn)非靜態(tài) synchronized 方法占用的鎖是當(dāng)前實(shí)例對(duì)象鎖吩谦;
修飾代碼塊:指定加鎖對(duì)象鸳谜,對(duì)給定對(duì)象加鎖膝藕,進(jìn)入同步代碼庫(kù)前要獲得給定對(duì)象的鎖。和 synchronized 方法一樣咐扭,synchronized(this) 代碼塊也是鎖定當(dāng)前對(duì)象的芭挽。synchronized 關(guān)鍵字加到 static 靜態(tài)方法和 synchronized(class) 代碼塊上都是是給 Class 類上鎖。這里再提一下:synchronized 關(guān)鍵字加到非 static 靜態(tài)方法上是給對(duì)象實(shí)例上鎖蝗肪。另外需要注意的是:盡量不要使用 synchronized(String a) 因?yàn)?JVM 中袜爪,字符串常量池具有緩沖功能。
Lock接口
實(shí)現(xiàn)類:
- ReentrantLock(可重入鎖 常用)
- ReentrantReadWriteLock.ReadLock(讀鎖)
- ReentrantReadWriteLock.WriteLock(寫(xiě)鎖)
public ReentrantLock() {
sync = new NonfairSync(); //非公平鎖
}
public ReentrantLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();//公平鎖
}
公平鎖:十分公平:可以先來(lái)后到
非公平鎖:十分不公平:可以插隊(duì) (默認(rèn))
Synchronized 和 Lock 區(qū)別
Synchronized是內(nèi)置的Java關(guān)鍵字薛闪, Lock 是一個(gè)接口
Synchronized無(wú)法判斷獲取鎖的狀態(tài)辛馆,Lock 可以判斷是否獲取到了鎖
Synchronized會(huì)自動(dòng)釋放鎖,lock 必須要手動(dòng)釋放鎖!如果不釋放鎖豁延,死鎖
Synchronized 線程 1(獲得鎖昙篙,阻塞)、線程2(等待诱咏,傻傻的等);Lock鎖就不一定會(huì)等待下去;
Synchronized 可重入鎖苔可,不可以中斷的,非公平;Lock 可重入鎖袋狞,可以判斷鎖焚辅,非公平(可以自己設(shè)置);
生產(chǎn)者和消費(fèi)者問(wèn)題
Synchronized實(shí)現(xiàn)
class Data1 {
private int number = 0;
public synchronized void increment() throws InterruptedException {
while (number != 0) {
this.wait();
}
number++;
System.out.println(Thread.currentThread().getName() + ":" + number);
this.notifyAll();
}
public synchronized void decrement() throws InterruptedException {
while (number != 1) {
this.wait();
}
number--;
System.out.println(Thread.currentThread().getName() + ":" + number);
this.notifyAll();
}
}
lock實(shí)現(xiàn)
Sychronized---->lock(Lock)
wait---->await(Condition)
notifiy---->signal(Condition)
class Data2 {
private int number = 0;
Lock lock = new ReentrantLock();
Condition condition = lock.newCondition();
public void increment() throws InterruptedException {
lock.lock();
try {
while (number != 0) {
condition.await();
}
number++;
System.out.println(Thread.currentThread().getName() + ":" + number);
condition.signalAll();
} finally {
lock.unlock();
}
}
public void decrement() throws InterruptedException {
lock.lock();
try {
while (number != 1) {
condition.await();
}
number--;
System.out.println(Thread.currentThread().getName() + ":" + number);
condition.signalAll();
} finally {
lock.unlock();
}
}
}
測(cè)試類
public class Test {
public static void main(String[] args) {
Data data = new Data();
new Thread(() -> {
for (int i = 0; i < 10; i++) {
try {
data.increment();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, "A").start();
new Thread(() -> {
for (int i = 0; i < 10; i++) {
try {
data.increment();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, "B").start();
new Thread(() -> {
for (int i = 0; i < 10; i++) {
try {
data.decrement();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, "C").start();
new Thread(() -> {
for (int i = 0; i < 10; i++) {
try {
data.decrement();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, "D").start();
}
}
注意(虛假喚醒)
當(dāng)一個(gè)條件滿足時(shí),很多線程都被喚醒了苟鸯,但是只有其中部分是有用的喚醒同蜻,其它的喚醒都是無(wú)用功。比如說(shuō)買(mǎi)貨早处,如果商品本來(lái)沒(méi)有貨物湾蔓,突然進(jìn)了一件商品,這是所有的線程都被喚醒了 陕赃,但是只能一個(gè)人買(mǎi)卵蛉,所以其他人都是假喚醒,獲取不到對(duì)象的鎖么库。
如何解決傻丝?必須將if改成while
Condition實(shí)現(xiàn)精準(zhǔn)通知和喚醒線程
目的:使喚醒順序?yàn)锳BC有序
class Data3{
int number =1; //1A 2B 3C
Lock lock=new ReentrantLock();
Condition condition1=lock.newCondition();
Condition condition2=lock.newCondition();
Condition condition3=lock.newCondition();
public void printA(){
lock.lock();
try {
while (number !=1){//2,3
condition1.await();
}
System.out.println(Thread.currentThread().getName()+"=>AAAAAAA");
number=2;
condition2.signal();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void printB(){
lock.lock();
try {
while (number !=2){//1,3
condition2.await();
}
System.out.println(Thread.currentThread().getName()+"=>BBBBBBB");
number=3;
condition3.signal();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void printC(){
lock.lock();
try {
while (number !=3){ //1,2
condition3.await();
}
System.out.println(Thread.currentThread().getName()+"=>CCCCCCC");
number=1;
condition1.signal();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
測(cè)試
public static void main(String[] args) {
Data3 data3 = new Data3();
new Thread(()->{
for (int i = 0; i < 10; i++) {
data3.printA();
}
},"A").start();
new Thread(()->{for (int i = 0; i < 10; i++) {
data3.printB();
}},"B").start();
new Thread(()->{for (int i = 0; i < 10; i++) {
data3.printC();
}},"C").start();
}
結(jié)果:
A=>AAAAAAA
B=>BBBBBBB
C=>CCCCCCC
A=>AAAAAAA
B=>BBBBBBB
C=>CCCCCCC
...