java高併發系列 - 第6天:執行緒的基本操作
新建執行緒
新建執行緒很簡單。只需要使用new關鍵字建立一個執行緒物件,然後呼叫它的start()啟動執行緒即可。
Thread thread1 = new Thread1();
t1.start();
那麼執行緒start()之後,會幹什麼呢?執行緒有個run()方法,start()會建立一個新的執行緒並讓這個執行緒執行run()方法。
這裡需要注意,下面程式碼也能通過編譯,也能正常執行。但是,卻不能新建一個執行緒,而是在當前執行緒中呼叫run()方法,將run方法只是作為一個普通的方法呼叫。
Thread thread = new Thread1();
thread1.run();
所以,希望大家注意,呼叫start方法和直接呼叫run方法的區別。
start方法是啟動一個執行緒,run方法只會在當前執行緒中序列的執行run方法中的程式碼。
預設情況下, 執行緒的run方法什麼都沒有,啟動一個執行緒之後馬上就結束了,所以如果你需要執行緒做點什麼,需要把您的程式碼寫到run方法中,所以必須重寫run方法。
Thread thread1 = new Thread() { @Override public void run() { System.out.println("hello,我是一個執行緒!"); } }; thread1.start();
上面是使用匿名內部類實現的,重寫了Thread的run方法,並且列印了一條資訊。我們可以通過繼承Thread類,然後重寫run方法,來自定義一個執行緒。但考慮java是單繼承的,從擴充套件性上來說,我們實現一個介面來自定義一個執行緒更好一些,java中剛好提供了Runnable介面來自定義一個執行緒。
@FunctionalInterface
public interface Runnable {
public abstract void run();
}
Thread類有一個非常重要的構造方法:
public Thread(Runnable target)
我們在看一下Thread的run方法:
public void run() {
if (target != null) {
target.run();
}
}
當我們啟動執行緒的start方法之後,執行緒會執行run方法,run方法中會呼叫Thread構造方法傳入的target的run方法。
實現Runnable介面是比較常見的做法,也是推薦的做法。
終止執行緒
一般來說執行緒執行完畢就會結束,無需手動關閉。但是如果我們想關閉一個正在執行的執行緒,有什麼方法呢?可以看一下Thread類中提供了一個stop()方法,呼叫這個方法,就可以立即將一個執行緒終止,非常方便。
package com.itsoku.chat01;
import lombok.extern.slf4j.Slf4j;
import java.util.concurrent.TimeUnit;
/**
* <b>description</b>: <br>
* <b>time</b>:2019/7/12 17:18 <br>
* <b>author</b>:微信公眾號:路人甲Java,專注於java技術分享(帶你玩轉 爬蟲、分散式事務、非同步訊息服務、任務排程、分庫分表、大資料等),喜歡請關注!
*/
@Slf4j
public class Demo01 {
public static void main(String[] args) throws InterruptedException {
Thread thread1 = new Thread() {
@Override
public void run() {
log.info("start");
boolean flag = true;
while (flag) {
;
}
log.info("end");
}
};
thread1.setName("thread1");
thread1.start();
//當前執行緒休眠1秒
TimeUnit.SECONDS.sleep(1);
//關閉執行緒thread1
thread1.stop();
//輸出執行緒thread1的狀態
log.info("{}", thread1.getState());
//當前執行緒休眠1秒
TimeUnit.SECONDS.sleep(1);
//輸出執行緒thread1的狀態
log.info("{}", thread1.getState());
}
}
執行程式碼,輸出:
18:02:15.312 [thread1] INFO com.itsoku.chat01.Demo01 - start
18:02:16.311 [main] INFO com.itsoku.chat01.Demo01 - RUNNABLE
18:02:17.313 [main] INFO com.itsoku.chat01.Demo01 - TERMINATED
程式碼中有個死迴圈,呼叫stop方法之後,執行緒thread1的狀態變為TERMINATED(結束狀態),執行緒停止了。
我們使用idea或者eclipse的時候,會發現這個方法是一個廢棄的方法,也就是說,在將來,jdk可能就會移除該方法。
stop方法為何會被廢棄而不推薦使用?stop方法過於暴力,強制把正在執行的方法停止了。
大家是否遇到過這樣的場景:電力系統需要維修,此時咱們正在寫程式碼,維修人員直接將電源關閉了,程式碼還沒儲存的,是不是很崩潰,這種方式就像直接呼叫執行緒的stop方法類似。執行緒正在執行過程中,被強制結束了,可能會導致一些意想不到的後果。可以給大家傳送一個通知,告訴大家儲存一下手頭的工作,將電腦關閉。
執行緒中斷
在java中,執行緒中斷是一種重要的執行緒寫作機制,從表面上理解,中斷就是讓目標執行緒停止執行的意思,實際上並非完全如此。在上面中,我們已經詳細討論了stop方法停止執行緒的壞處,jdk中提供了更好的中斷執行緒的方法。嚴格的說,執行緒中斷並不會使執行緒立即退出,而是給執行緒傳送一個通知,告知目標執行緒,有人希望你退出了!至於目標執行緒接收到通知之後如何處理,則完全由目標執行緒自己決定,這點很重要,如果中斷後,執行緒立即無條件退出,我們又會到stop方法的老問題。
Thread提供了3個與執行緒中斷有關的方法,這3個方法容易混淆,大家注意下:
public void interrupt() //中斷執行緒
public boolean isInterrupted() //判斷執行緒是否被中斷
public static boolean interrupted() //判斷執行緒是否被中斷,並清除當前中斷狀態
interrupt()方法是一個例項方法,它通知目標執行緒中斷,也就是設定中斷標誌位為true,中斷標誌位表示當前執行緒已經被中斷了。isInterrupted()方法也是一個例項方法,它判斷當前執行緒是否被中斷(通過檢查中斷標誌位)。最後一個方法interrupted()是一個靜態方法,返回boolean型別,也是用來判斷當前執行緒是否被中斷,但是同時會清除當前執行緒的中斷標誌位的狀態。
while (true) {
if (this.isInterrupted()) {
System.out.println("我要退出了!");
break;
}
}
}
};
thread1.setName("thread1");
thread1.start();
TimeUnit.SECONDS.sleep(1);
thread1.interrupt();
上面程式碼中有個死迴圈,interrupt()方法被呼叫之後,執行緒的中斷標誌將被置為true,迴圈體中通過檢查執行緒的中斷標誌是否為ture(this.isInterrupted()
)來判斷執行緒是否需要退出了。
再看一種中斷的方法:
static volatile boolean isStop = false;
public static void main(String[] args) throws InterruptedException {
Thread thread1 = new Thread() {
@Override
public void run() {
while (true) {
if (isStop) {
System.out.println("我要退出了!");
break;
}
}
}
};
thread1.setName("thread1");
thread1.start();
TimeUnit.SECONDS.sleep(1);
isStop = true;
}
程式碼中通過一個變數isStop來控制執行緒是否停止。
通過變數控制和執行緒自帶的interrupt方法來中斷執行緒有什麼區別呢?
如果一個執行緒呼叫了sleep方法,一直處於休眠狀態,通過變數控制,還可以中斷執行緒麼?大家可以思考一下。
此時只能使用執行緒提供的interrupt方法來中斷執行緒了。
public static void main(String[] args) throws InterruptedException {
Thread thread1 = new Thread() {
@Override
public void run() {
while (true) {
//休眠100秒
try {
TimeUnit.SECONDS.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("我要退出了!");
break;
}
}
};
thread1.setName("thread1");
thread1.start();
TimeUnit.SECONDS.sleep(1);
thread1.interrupt();
}
呼叫interrupt()方法之後,執行緒的sleep方法將會丟擲InterruptedException
異常。
Thread thread1 = new Thread() {
@Override
public void run() {
while (true) {
//休眠100秒
try {
TimeUnit.SECONDS.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
if (this.isInterrupted()) {
System.out.println("我要退出了!");
break;
}
}
}
};
執行上面的程式碼,發現程式無法終止。為什麼?
程式碼需要改為:
Thread thread1 = new Thread() {
@Override
public void run() {
while (true) {
//休眠100秒
try {
TimeUnit.SECONDS.sleep(100);
} catch (InterruptedException e) {
this.interrupt();
e.printStackTrace();
}
if (this.isInterrupted()) {
System.out.println("我要退出了!");
break;
}
}
}
};
上面程式碼可以終止。
注意:sleep方法由於中斷而丟擲異常之後,執行緒的中斷標誌會被清除(置為false),所以在異常中需要執行this.interrupt()方法,將中斷標誌位置為true
等待(wait)和通知(notify)
為了支援多執行緒之間的協作,JDK提供了兩個非常重要的方法:等待wait()方法和通知notify()方法。這2個方法並不是在Thread類中的,而是在Object類中定義的。這意味著所有的物件都可以呼叫者兩個方法。
public final void wait() throws InterruptedException;
public final native void notify();
當在一個物件例項上呼叫wait()方法後,當前執行緒就會在這個物件上等待。這是什麼意思?比如線上程A中,呼叫了obj.wait()方法,那麼執行緒A就會停止繼續執行,轉為等待狀態。等待到什麼時候結束呢?執行緒A會一直等到其他執行緒呼叫obj.notify()方法為止,這時,obj物件成為了多個執行緒之間的有效通訊手段。
那麼wait()方法和notify()方法是如何工作的呢?如圖2.5展示了兩者的工作過程。如果一個執行緒呼叫了object.wait()方法,那麼它就會進出object物件的等待佇列。這個佇列中,可能會有多個執行緒,因為系統可能執行多個執行緒同時等待某一個物件。當object.notify()方法被呼叫時,它就會從這個佇列中隨機選擇一個執行緒,並將其喚醒。這裡希望大家注意一下,這個選擇是不公平的,並不是先等待執行緒就會優先被選擇,這個選擇完全是隨機的。
除notify()方法外,Object獨享還有一個nofiyAll()方法,它和notify()方法的功能類似,不同的是,它會喚醒在這個等待佇列中所有等待的執行緒,而不是隨機選擇一個。
這裡強調一點,Object.wait()方法並不能隨便呼叫。它必須包含在對應的synchronize語句彙總,無論是wait()方法或者notify()方法都需要首先獲取目標獨享的一個監視器。圖2.6顯示了wait()方法和nofiy()方法的工作流程細節。其中T1和T2表示兩個執行緒。T1在正確執行wait()方法錢,必須獲得object物件的監視器。而wait()方法在執行後,會釋放這個監視器。這樣做的目的是使其他等待在object物件上的執行緒不至於因為T1的休眠而全部無法正常執行。
執行緒T2在notify()方法呼叫前,也必須獲得object物件的監視器。所幸,此時T1已經釋放了這個監視器,因此,T2可以順利獲得object物件的監視器。接著,T2執行了notify()方法嘗試喚醒一個等待執行緒,這裡假設喚醒了T1。T1在被喚醒後,要做的第一件事並不是執行後續程式碼,而是要嘗試重新獲得object物件的監視器,而這個監視器也正是T1在wait()方法執行前所持有的那個。如果暫時無法獲得,則T1還必須等待這個監視器。當監視器順利獲得後,T1才可以在真正意義上繼續執行。
給大家上個例子:
package com.itsoku.chat01;
/**
* <b>description</b>: <br>
* <b>time</b>:2019/7/12 17:18 <br>
* <b>author</b>:微信公眾號:路人甲Java,專注於java技術分享(帶你玩轉 爬蟲、分散式事務、非同步訊息服務、任務排程、分庫分表、大資料等),喜歡請關注!
*/
public class Demo06 {
static Object object = new Object();
public static class T1 extends Thread {
@Override
public void run() {
synchronized (object) {
System.out.println(System.currentTimeMillis() + ":T1 start!");
try {
System.out.println(System.currentTimeMillis() + ":T1 wait for object");
object.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(System.currentTimeMillis() + ":T1 end!");
}
}
}
public static class T2 extends Thread {
@Override
public void run() {
synchronized (object) {
System.out.println(System.currentTimeMillis() + ":T2 start,notify one thread! ");
object.notify();
System.out.println(System.currentTimeMillis() + ":T2 end!");
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public static void main(String[] args) throws InterruptedException {
new T1().start();
new T2().start();
}
}
執行結果:
1562934497212:T1 start!
1562934497212:T1 wait for object
1562934497212:T2 start,notify one thread!
1562934497212:T2 end!
1562934499213:T1 end!
注意下列印結果,T2呼叫notify方法之後,T1並不能立即繼續執行,而是要等待T2釋放objec投遞鎖之後,T1重新成功獲取鎖後,才能繼續執行。因此最後2行日誌相差了2秒(因為T2呼叫notify方法後休眠了2秒)。
注意:Object.wait()方法和Thread.sleeep()方法都可以讓現場等待若干時間。除wait()方法可以被喚醒外,另外一個主要的區別就是wait()方法會釋放目標物件的鎖,而Thread.sleep()方法不會釋放鎖。
再給大家講解一下wait(),notify(),notifyAll(),加深一下理解:
可以這麼理解,obj物件上有2個佇列,如圖1,q1:等待佇列,q2:準備獲取鎖的佇列;兩個佇列都為空。
obj.wait()過程:
synchronize(obj){
obj.wait();
}
假如有3個執行緒,t1、t2、t3同時執行上面程式碼,t1、t2、t3會進入q2佇列,如圖2,進入q2的佇列的這些執行緒才有資格去爭搶obj的鎖,假設t1爭搶到了,那麼t2、t3機型在q2中等待著獲取鎖,t1進入程式碼塊執行wait()方法,此時t1會進入q1佇列,然後系統會通知q2佇列中的t2、t3去爭搶obj的鎖,搶到之後過程如t1的過程。最後t1、t2、t3都進入了q1佇列,如圖3。
上面過程之後,又來了執行緒t4執行了notify()方法,如下:**
synchronize(obj){
obj.notify();
}
t4會獲取到obj的鎖,然後執行notify()方法,系統會從q1佇列中隨機取一個執行緒,將其加入到q2佇列,假如t2運氣比較好,被隨機到了,然後t2進入了q2佇列,如圖4,進入q2的佇列的鎖才有資格爭搶obj的鎖,t4執行緒執行完畢之後,會釋放obj的鎖,此時佇列q2中的t2會獲取到obj的鎖,然後繼續執行,執行完畢之後,q1中包含t1、t3,q2佇列為空,如圖5
接著又來了個t5佇列,執行了notifyAll()方法,如下:
synchronize(obj){
obj.notifyAll();
}
2.呼叫obj.wait()方法,當前執行緒會加入佇列queue1,然後會釋放obj物件的鎖
t5會獲取到obj的鎖,然後執行notifyAll()方法,系統會將佇列q1中的執行緒都移到q2中,如圖6,t5執行緒執行完畢之後,會釋放obj的鎖,此時佇列q2中的t1、t3會爭搶obj的鎖,爭搶到的繼續執行,未增強到的帶鎖釋放之後,系統會通知q2中的執行緒繼續爭搶索,然後繼續執行,最後兩個佇列中都為空了。
掛起(suspend)和繼續執行(resume)執行緒
Thread類中還有2個方法,即執行緒掛起(suspend)和繼續執行(resume),這2個操作是一對相反的操作,被掛起的執行緒,必須要等到resume()方法操作後,才能繼續執行。系統中已經標註著2個方法過時了,不推薦使用。
系統不推薦使用suspend()方法去掛起執行緒是因為suspend()方法導致執行緒暫停的同時,並不會釋放任何鎖資源。此時,其他任何執行緒想要訪問被它佔用的鎖時,都會被牽連,導致無法正常執行(如圖2.7所示)。直到在對應的執行緒上進行了resume()方法操作,被掛起的執行緒才能繼續,從而其他所有阻塞在相關鎖上的執行緒也可以繼續執行。但是,如果resume()方法操作意外地在suspend()方法前就被執行了,那麼被掛起的執行緒可能很難有機會被繼續執行了。並且,更嚴重的是:它所佔用的鎖不會被釋放,因此可能會導致整個系統工作不正常。而且,對於被掛起的執行緒,從它執行緒的狀態上看,居然還是Runnable狀態,這也會影響我們隊系統當前狀態的判斷。
上個例子:
/**
* <b>description</b>: <br>
* <b>time</b>:2019/7/12 17:18 <br>
* <b>author</b>:微信公眾號:路人甲Java,專注於java技術分享(帶你玩轉 爬蟲、分散式事務、非同步訊息服務、任務排程、分庫分表、大資料等),喜歡請關注!
*/
public class Demo07 {
static Object object = new Object();
public static class T1 extends Thread {
public T1(String name) {
super(name);
}
@Override
public void run() {
synchronized (object) {
System.out.println("in " + this.getName());
Thread.currentThread().suspend();
}
}
}
public static void main(String[] args) throws InterruptedException {
T1 t1 = new T1("t1");
t1.start();
Thread.sleep(100);
T1 t2 = new T1("t2");
t2.start();
t1.resume();
t2.resume();
t1.join();
t2.join();
}
}
執行程式碼輸出:
in t1
in t2
我們會發現程式不會結束,執行緒t2被掛起了,導致程式無法結束,使用jstack命令檢視執行緒堆疊資訊可以看到:
"t2" #13 prio=5 os_prio=0 tid=0x000000002796c000 nid=0xa3c runnable [0x000000002867f000]
java.lang.Thread.State: RUNNABLE
at java.lang.Thread.suspend0(Native Method)
at java.lang.Thread.suspend(Thread.java:1029)
at com.itsoku.chat01.Demo07$T1.run(Demo07.java:20)
- locked <0x0000000717372fc0> (a java.lang.Object)
發現t2執行緒在suspend0處被掛起了,t2的狀態竟然還是RUNNABLE狀態,執行緒明明被掛起了,狀態還是執行中容易導致我們隊當前系統進行誤判,程式碼中已經呼叫resume()方法了,但是由於時間先後順序的緣故,resume並沒有生效,這導致了t2永遠滴被掛起了,並且永遠佔用了object的鎖,這對於系統來說可能是致命的。
等待執行緒結束(join)和謙讓(yeild)
很多時候,一個執行緒的輸入可能非常依賴於另外一個或者多個執行緒的輸出,此時,這個執行緒就需要等待依賴的執行緒執行完畢,才能繼續執行。jdk提供了join()操作來實現這個功能。如下所示,顯示了2個join()方法:
public final void join() throws InterruptedException;
public final synchronized void join(long millis) throws InterruptedException;
第1個方法表示無限等待,它會一直只是當前執行緒。知道目標執行緒執行完畢。
第2個方法有個引數,用於指定等待時間,如果超過了給定的時間目標執行緒還在執行,當前執行緒也會停止等待,而繼續往下執行。
比如:執行緒T1需要等待T2、T3完成之後才能繼續執行,那麼在T1執行緒中需要分別呼叫T2和T3的join()方法。
上個示例:
/**
* <b>description</b>: <br>
* <b>time</b>:2019/7/12 17:18 <br>
* <b>author</b>:微信公眾號:路人甲Java,專注於java技術分享(帶你玩轉 爬蟲、分散式事務、非同步訊息服務、任務排程、分庫分表、大資料等),喜歡請關注!
*/
public class Demo08 {
static int num = 0;
public static class T1 extends Thread {
public T1(String name) {
super(name);
}
@Override
public void run() {
System.out.println(System.currentTimeMillis() + ",start " + this.getName());
for (int i = 0; i < 10; i++) {
num++;
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(System.currentTimeMillis() + ",end " + this.getName());
}
}
public static void main(String[] args) throws InterruptedException {
T1 t1 = new T1("t1");
t1.start();
t1.join();
System.out.println(System.currentTimeMillis() + ",num = " + num);
}
}
執行結果:
1562939889129,start t1
1562939891134,end t1
1562939891134,num = 10
num的結果為10,1、3行的時間戳相差2秒左右,說明主執行緒等待t1完成之後才繼續執行的。
看一下jdk1.8中Thread.join()方法的實現:
public final synchronized void join(long millis) throws InterruptedException {
long base = System.currentTimeMillis();
long now = 0;
if (millis < 0) {
throw new IllegalArgumentException("timeout value is negative");
}
if (millis == 0) {
while (isAlive()) {
wait(0);
}
} else {
while (isAlive()) {
long delay = millis - now;
if (delay <= 0) {
break;
}
wait(delay);
now = System.currentTimeMillis() - base;
}
}
}
從join的程式碼中可以看出,在被等待的執行緒上使用了synchronize,呼叫了它的wait()方法,執行緒最後執行完畢之後,系統會自動呼叫它的notifyAll()方法,喚醒所有在此執行緒上等待的其他執行緒。
注意:被等待的執行緒執行完畢之後,系統自動會呼叫該執行緒的notifyAll()方法。所以一般情況下,我們不要去線上程物件上使用wait()、notify()、notifyAll()方法。
另外一個方法是Thread.yield(),他的定義如下:
public static native void yield();
yield是謙讓的意思,這是一個靜態方法,一旦執行,它會讓當前執行緒出讓CPU,但需要注意的是,出讓CPU並不是說不讓當前執行緒執行了,當前執行緒在出讓CPU後,還會進行CPU資源的爭奪,但是能否再搶到CPU的執行權就不一定了。因此,對Thread.yield()方法的呼叫好像就是在說:我已經完成了一些主要的工作,我可以休息一下了,可以讓CPU給其他執行緒一些工作機會了。
如果覺得一個執行緒不太重要,或者優先順序比較低,而又擔心此執行緒會過多的佔用CPU資源,那麼可以在適當的時候呼叫一下Thread.yield()方法,給與其他執行緒更多的機會。
總結
- 建立執行緒的2中方式:繼承Thread類;實現Runnable介面
- 啟動執行緒:呼叫執行緒的start()方法
- 終止執行緒:呼叫執行緒的stop()方法,方法已過時,建議不要使用
- 執行緒中斷相關的方法:呼叫執行緒例項interrupt()方法將中斷標誌置為true;使用執行緒例項方法isInterrupted()獲取中斷標誌;呼叫Thread的靜態方法interrupted()獲取執行緒是否被中斷,此方法呼叫之後會清除中斷標誌(將中斷標誌置為false了)
- wait、notify、notifyAll方法,這塊比較難理解,可以回過頭去再理理
- 執行緒掛起使用執行緒例項方法suspend(),恢復執行緒使用執行緒例項方法resume(),這2個方法都過時了,不建議使用
- 等待執行緒結束:呼叫執行緒例項方法join()
- 出讓cpu資源:呼叫執行緒靜態方法yeild()
java高併發系列交流群
相關推薦
java高併發系列 - 第6天:執行緒的基本操作
新建執行緒 新建執行緒很簡單。只需要使用new關鍵字建立一個執行緒物件,然後呼叫它的start()啟動執行緒即可。 Thread thread1 = new Thread1(); t1.start(); 那麼執行緒start()之後,會幹什麼呢?執行緒有個run()方法,start()會建立一個新的執行緒並讓
java高併發系列 - 第31天:獲取執行緒執行結果,這6種方法你都知道?
這是java高併發系列第31篇。 環境:jdk1.8。 java高併發系列已經學了不少東西了,本篇文章,我們用前面學的知識來實現一個需求: 在一個執行緒中需要獲取其他執行緒的執行結果,能想到幾種方式?各有什麼優缺點? 結合這個需求,我們使用6種方式,來對之前學過的知識點做一個回顧,加深記憶。 方式1:Thre
java高併發系列 - 第16天:JUC中等待多執行緒完成的工具類CountDownLatch,必備技能
這是java高併發系列第16篇文章。 本篇內容 介紹CountDownLatch及使用場景 提供幾個示例介紹CountDownLatch的使用 手寫一個並行處理任務的工具類 假如有這樣一個需求,當我們需要解析一個Excel裡多個sheet的資料時,可以考慮使用多執行緒,每個執行緒解析一個sheet裡的資料
java高併發系列 - 第17天:JUC中的迴圈柵欄CyclicBarrier常見的6種使用場景及程式碼示例
這是java高併發系列第17篇。 本文主要內容: 介紹CyclicBarrier 6個示例介紹CyclicBarrier的使用 對比CyclicBarrier和CountDownLatch CyclicBarrier簡介 CyclicBarrier通常稱為迴圈屏障。它和CountDownLatch很相似,
java高併發系列-第1天:必須知道的幾個概念
java高併發系列-第1天:必須知道的幾個概念 同步(Synchronous)和非同步(Asynchronous) 同步和非同步通常來形容一次方法呼叫,同步方法呼叫一旦開始,呼叫者必須等到方法呼叫返回後,才能繼續後續的行為。非同步方法呼叫更像一個訊息傳遞,一旦開始,方法呼叫就會立即返回,呼叫者就可以繼續後續的
java高併發系列 - 第12天JUC:ReentrantLock重入鎖
java高併發系列 - 第12天JUC:ReentrantLock重入鎖 本篇文章開始將juc中常用的一些類,估計會有十來篇。 synchronized的侷限性 synchronized是java內建的關鍵字,它提供了一種獨佔的加鎖方式。synchronized的獲取和釋放鎖由jvm實現,使用者不需要顯示的釋
java高併發系列 - 第14天:JUC中的LockSupport工具類,必備技能
這是java高併發系列第14篇文章。 本文主要內容: 講解3種讓執行緒等待和喚醒的方法,每種方法配合具體的示例 介紹LockSupport主要用法 對比3種方式,瞭解他們之間的區別 LockSupport位於java.util.concurrent(簡稱juc)包中,算是juc中一個基礎類,juc中很多地
java高併發系列 - 第15天:JUC中的Semaphore,最簡單的限流工具類,必備技能
這是java高併發系列第15篇文章 Semaphore(訊號量)為多執行緒協作提供了更為強大的控制方法,前面的文章中我們學了synchronized和重入鎖ReentrantLock,這2種鎖一次都只能允許一個執行緒訪問一個資源,而訊號量可以控制有多少個執行緒可以同時訪問特定的資源。 Semaphore常用
java高併發系列 - 第21天:java中的CAS操作,java併發的基石
這是java高併發系列第21篇文章。 本文主要內容 從網站計數器實現中一步步引出CAS操作 介紹java中的CAS及CAS可能存在的問題 悲觀鎖和樂觀鎖的一些介紹及資料庫樂觀鎖的一個常見示例 使用java中的原子操作實現網站計數器功能 我們需要解決的問題 需求:我們開發了一個網站,需要對訪問量進行統計,使
java高併發系列 - 第22天:java中底層工具類Unsafe,高手必須要了解
這是java高併發系列第22篇文章,文章基於jdk1.8環境。 本文主要內容 基本介紹 通過反射獲取Unsafe例項 Unsafe中的CAS操作 Unsafe中原子操作相關方法介紹 Unsafe中執行緒排程相關方法 park和unpark示例 Unsafe鎖示例 Unsafe中保證變數的可見性 Unsafe
java高併發系列 - 第23天:JUC中原子類,一篇就夠了
這是java高併發系列第23篇文章,環境:jdk1.8。 本文主要內容 JUC中的原子類介紹 介紹基本型別原子類 介紹陣列型別原子類 介紹引用型別原子類 介紹物件屬性修改相關原子類 預備知識 JUC中的原子類都是都是依靠volatile、CAS、Unsafe類配合來實現的,需要了解的請移步: volati
java高併發系列 - 第24天:ThreadLocal、InheritableThreadLocal(通俗易懂)
java高併發系列第24篇文章。 環境:jdk1.8。 本文內容 需要解決的問題 介紹ThreadLocal 介紹InheritableThreadLocal 需要解決的問題 我們還是以解決問題的方式來引出ThreadLocal、InheritableThreadLocal,這樣印象會深刻一些。 目前
java高併發系列 - 第25天:掌握JUC中的阻塞佇列
這是java高併發系列第25篇文章。 環境:jdk1.8。 本文內容 掌握Queue、BlockingQueue介面中常用的方法 介紹6中阻塞佇列,及相關場景示例 重點掌握4種常用的阻塞佇列 Queue介面 佇列是一種先進先出(FIFO)的資料結構,java中用Queue介面來表示佇列。 Queue介面中
java高併發系列 - 第27天:實戰篇,介面效能成倍提升,讓同事刮目相看,現學現用
這是java高併發系列第27篇文章。 開發環境:jdk1.8。 案例講解 電商app都有用過吧,商品詳情頁,需要給他們提供一個介面獲取商品相關資訊: 商品基本資訊(名稱、價格、庫存、會員價格等) 商品圖片列表 商品描述資訊(描述資訊一般是由富文字編輯的大文字資訊) 資料庫中我們用了3張表儲存上面的資訊:
java高併發系列 - 第32天:高併發中計數器的實現方式有哪些?
這是java高併發系列第32篇文章。 java環境:jdk1.8。 本文主要內容 4種方式實現計數器功能,對比其效能 介紹LongAdder 介紹LongAccumulator 需求:一個jvm中實現一個計數器功能,需保證多執行緒情況下資料正確性。 我們來模擬50個執行緒,每個執行緒對計數器遞增100萬次
實戰Java高併發程式設計(3.2 執行緒池)
1.Executor jdk提供了一套Executor框架,本質上是一個執行緒池。 newFixedThreadPool()方法:該方法返回一個固定數量的執行緒池。該執行緒池中的執行緒數量始終不變,當有一個新任務提交時,執行緒池中若有空閒執行緒,則立即執行,若沒有,則任務會暫存在一個任
JAVA高併發(二)------區分執行緒和程序
執行緒與程序 程序是計算機中的程式關於某資料集合上的一次執行活動,是系統進行資源分配和排程的基本單位,是作業系統結構的基礎,在早期面向程序設計的計算機結構中,程序是程式的基本執行實體,在當代面向執行緒的計算機結構中,程序是執行緒的容器,程式是指令資料及其組織形式的描述,程序
java併發系列 - 第29天:高併發中常見的限流方式
這是java高併發系列第29篇。 環境:jdk1.8。 本文內容 介紹常見的限流演算法 通過控制最大併發數來進行限流 通過漏桶演算法來進行限流 通過令牌桶演算法來進行限流 限流工具類RateLimiter 常見的限流的場景 秒殺活動,數量有限,訪問量巨大,為了防止系統宕機,需要做限流處理 國慶期間,一般
JAVA入門到精通-第38講-執行緒-坦克大戰6
應用程式在記憶體中開闢空間-程序; 記憶體-大腦容量; 反應切換的速度-CPU; 試圖去嘗試別的程序的地址空間---病毒;  
Go 系列教程-6 多執行緒 併發
Go 系列教程 —— 20. 併發入門 Go 是併發式語言,而不是並行式語言。在討論 Go 如何處理併發之前,我們必須理解何為併發,以及併發與並行的區別。 併發是什麼? 併發是指立即處理多個任務的能力。一個例子就能很好地說明這一點。 我們可以想象一個人正在跑步。假如在他晨跑時,鞋帶突