您好,登錄后才能下訂單哦!
這篇文章將為大家詳細(xì)講解有關(guān)java中Thread API的調(diào)用規(guī)則是什么,文章內(nèi)容質(zhì)量較高,因此小編分享給大家做個參考,希望大家閱讀完這篇文章后對相關(guān)知識有一定的了解。
Thread中有兩個方法,一個是start方法,一個是run方法,兩個都可以調(diào)用,那么兩個有什么區(qū)別呢?
先看一下start方法:
public synchronized void start() { if (threadStatus != 0) throw new IllegalThreadStateException(); group.add(this); boolean started = false; try { start0(); started = true; } finally { try { if (!started) { group.threadStartFailed(this); } } catch (Throwable ignore) { } } } private native void start0();
start()是一個synchronized的方法,通過它會去調(diào)用native的start0方法,而最終將會調(diào)用Thread的run()方法。
我們知道,創(chuàng)建一個Thread有兩種方式,一種是傳入一個Runnable,一個是繼承Thread,并重寫run()方法。
如果我們直接調(diào)用Thread的run()方法會發(fā)生什么事情呢?
先看一下run方法的定義:
public void run() { if (target != null) { target.run(); } }
默認(rèn)情況下, 這個target就是一個Runnable對象,如果Thread是通過Runnable來構(gòu)建的話,調(diào)用Thread.run()會在當(dāng)前線程中運(yùn)行run方法中的內(nèi)容。
如果Thread是以其形式構(gòu)建,并且沒有重新run()方法,那么直接調(diào)用Thread.run()將什么都不會做。
public void wrongStart(){ Runnable runnable= ()-> System.out.println("in thread running!"); Thread thread= new Thread(runnable); thread.run(); } public void correctStart(){ Runnable runnable= ()-> System.out.println("in thread running!"); Thread thread= new Thread(runnable); thread.start(); }
所以,上面兩種調(diào)用方式,只有第二種是正確的。
Thread中有個字段類型是java.lang.ThreadGroup,這個主要是用來給Thread進(jìn)行分組,我們看下Thread的這個構(gòu)造函數(shù):
public Thread(ThreadGroup group, Runnable target) { this(group, target, "Thread-" + nextThreadNum(), 0); }
上面的構(gòu)造函數(shù)可以在傳入runnable的同時傳遞一個ThreadGroup對Thread進(jìn)行分組。
如果沒有指定ThreadGroup,那么將會為其分配一個默認(rèn)的default group。
ThreadGroup是做什么的呢?ThreadGroup是java 1.0引入的方法,主要是一次性的對一組thread進(jìn)行操作。我們可以調(diào)用ThreadGroup.interrupt()來一次性的對整個Group的Thread進(jìn)行interrupts操作。
雖然ThreadGroup提供了很多有用的方法,但是其中很多方法都被廢棄了,比如:allowThreadSuspension(), resume(), stop(), 和 suspend(),并且ThreadGroup中還有很多方法是非線程安全的:
ThreadGroup.activeCount()
這個方法主要是用來統(tǒng)計一個ThreadGroup中活動的線程個數(shù),這個方法會統(tǒng)計還未啟動的線程,同時也會受系統(tǒng)線程的影響,所以是不準(zhǔn)確的。
ThreadGroup.enumerate()
這個方法是將ThreadGroup和子group的線程拷貝到一個數(shù)組中,但是如果數(shù)組太小了,多余的線程是會被自動忽略的。
ThreadGroup本身有一個 stop() 方法用來停止所有的線程,但是stop是不安全的,已經(jīng)被廢棄了。
那么我們該怎么去安全的停止很多個線程呢?
使用executor.shutdown()就可以了。
剛剛講了ThreadGroup中不要調(diào)用stop()方法,因為stop是不安全的。
調(diào)用stop方法會立馬釋放線程持有的所有的鎖,并且會拋出ThreadDeath異常。
因為會釋放所有的鎖,所以可能會造成受這些鎖保護(hù)的對象的狀態(tài)發(fā)生不一致的情況。
替代的方法有兩種,一種是使用volatile flag變量,來控制線程的循環(huán)執(zhí)行:
private volatile boolean done = false; public void shutDown(){ this.done= true; } public void stopWithFlag(){ Runnable runnable= ()->{ while(!done){ System.out.println("in Runnable"); } }; Thread thread= new Thread(runnable); thread.start(); shutDown(); }
另外一種方法就是調(diào)用interrupt(), 這里我們要注意interrupt()的使用要點:
如果當(dāng)前線程實例在調(diào)用Object類的wait(),wait(long)或wait(long,int)方法或join(),join(long),join(long,int)方法,或者在該實例中調(diào)用了Thread.sleep(long)或Thread.sleep(long,int)方法,并且正在阻塞狀態(tài)中時,則其中斷狀態(tài)將被清除,并將收到InterruptedException。
如果此線程在InterruptibleChannel上的I/O操作中處于被阻塞狀態(tài),則該channel將被關(guān)閉,該線程的中斷狀態(tài)將被設(shè)置為true,并且該線程將收到j(luò)ava.nio.channels.ClosedByInterruptException異常。
如果此線程在java.nio.channels.Selector中處于被被阻塞狀態(tài),則將設(shè)置該線程的中斷狀態(tài)為true,并且它將立即從select操作中返回。
如果上面的情況都不成立,則設(shè)置中斷狀態(tài)為true。
先看下面的例子:
public static void main(String[] args) { Runnable runnable= ()->{ while (!Thread.interrupted()) { System.out.println("in thread"); } }; Thread thread= new Thread(runnable); thread.start(); Thread.sleep(5000); thread.interrupt(); }
我們在while循環(huán)中調(diào)用了Thread.interrupted()方法用來判斷線程是否被設(shè)置了中斷位,然后在main方法中調(diào)用了thread.interrupt()來設(shè)置中斷,最終可以正確的停止Thread。
注意,這里運(yùn)行的Thread并沒有被阻塞,所以并不滿足我們上面提到的第一個條件。
下面我們再看一個例子:
public static void main(String[] args) { Runnable runnable= ()->{ while (!Thread.interrupted()) { System.out.println("in thread"); try { Thread.sleep(5000); } catch (InterruptedException e) { e.printStackTrace(); } } }; Thread thread= new Thread(runnable); thread.start(); thread.interrupt(); }
這個例子和上面的例子不同之處就是在于,Thread中調(diào)用了sleep方法,導(dǎo)致Thread被阻塞了,最終滿足了第一個條件,從而不會設(shè)置終端位,只會拋出InterruptedException,所以這個例子中線程是不會被停止的,大家一定要注意。
為什么要放在循環(huán)中呢?因為我們希望wait不是被錯誤的被喚醒,所以我們需要在wait被喚醒之后,重新檢測一遍條件。
錯誤的調(diào)用是放在if語句中:
synchronized (object) { if (<condition does not hold>) { object.wait(); } // Proceed when condition holds }
正確的方法是放在while循環(huán)中:
synchronized (object) { while (<condition does not hold>) { object.wait(); } // Proceed when condition holds }
關(guān)于java中Thread API的調(diào)用規(guī)則是什么就分享到這里了,希望以上內(nèi)容可以對大家有一定的幫助,可以學(xué)到更多知識。如果覺得文章不錯,可以把它分享出去讓更多的人看到。
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。