執行緒的基本操作:新建和終止執行緒
執行緒的基本操作
新建執行緒
Java官方只在文件中宣告瞭兩種建立執行緒的方式,一種是繼承Thread類,一種是實現Runnable介面。
至於其他方式,Callable ,執行緒池,Future等建立執行緒的方式底層還是通過這兩種方式實現的。
- 可以用Thread類建立一個新執行緒。
- 然後呼叫該執行緒的start方法。
class MyThread extends Thread{
//重寫run方法
public void run() {
//....
}
}
public static void main(String[]args)
{
Thread thread = new MyThread();//執行緒狀態為NEW
thread.start();//通知執行緒可以啟動,當執行緒真正獲得CPU時,狀態轉為RUNNABLE
}
- 使用Runnable 介面建立一個執行緒
- 然後呼叫該執行緒的start方法。
public static void main(String[]args)
{
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
//...邏輯程式碼
}
});
thread.start();
}
Thread類有關初始化執行緒和start方法部分原始碼:
在new了一個Thread類之後,在空參構造方法中會呼叫一個私有的方法init,然後他有一個過載的方法,init方法主要是設定一些執行緒的基本資訊的,包括所屬執行緒組,執行緒名字,是否是守護執行緒,執行緒的優先順序等。
如果不在new時傳遞執行緒的名字,會預設賦值一個執行緒名"Thread-" + nextThreadNum();
這裡的nextThreadNum方法是一個同步方法,它可以保證建立出來的每個執行緒都有不同的名字。內部其實只做了一個整數的++操作。
run()方法的實現的呼叫方式與是否實現Runnable介面有關係。
start()方法可以看到只有當threadStatus == 0才會執行成功,否則會拋IllegalThreadStateException,這裡的threadStatus就是之前寫過的用來儲存執行緒狀態的成員,至於當他為0時狀態是什麼,我們可以看看列舉類State中定義了一個方法getState(),然後內部呼叫了sun.misc.VM.toThreadState(threadStatus);方法,跟進這個方法,發現threadStatus==0時表示的是NEW狀態。所以說一個執行緒只能呼叫一次start()方法,而且只能是NEW狀態時呼叫。start方法也是一個同步方法,他不知道通過synchronized關鍵字來保證執行緒安全,內部還設定了一個標誌位started 來保證執行緒真正啟動時的安全。至於為什麼呼叫start方法後會執行重寫的run方法,對應的程式碼邏輯應該寫在了本地方法start0中,說到本地方法,Thread類中也為本地方法提供了註冊方法registerNatives(),這個方法也在Object類中出現過。
關於執行緒的優先順序通過執行緒內部的一個私有變數priority設定,可以通過setPriority(int) 來設定,但是通過設定執行緒的優先順序並不能保證執行緒執行的正確性,因為雖然在java中設定了多執行緒的優先順序。但是有的作業系統會忽略掉Java程式中所設定的優先順序。比如說Mac OS 10.10就將所有的Java程式中的執行緒的優先順序都設定為5。所以說有時候雖然設定了優先順序,但是執行緒真正的執行順序並不會按照所設定優先順序的順序來執行。
class Thread implements Runnable {
private volatile String name;
private int priority;//執行緒的優先順序。預設是5,範圍是1-10
//空參構造方式
public Thread() {
init(null, null, "Thread-" + nextThreadNum(), 0);
}
//空參建構函式
private void init(ThreadGroup g, Runnable target, String name,
long stackSize) {
init(g, target, name, stackSize, null, true);
}
//實現Runnable介面呼叫的建構函式。
public Thread(Runnable target) {
init(null, target, "Thread-" + nextThreadNum(), 0);
}
/* run方法內部實現,繼承Thread類需要重寫,
實現Runnable介面呼叫的還是Runnable介面的實現類的閏方法
*/
public void run() {
if (target != null) {
target.run();
}
}
//保證執行緒名字不同
private static synchronized int nextThreadNum() {
return threadInitNumber++;
}
//初始化執行緒
private void init(ThreadGroup g, Runnable target, String name,
long stackSize, AccessControlContext acc,
boolean inheritThreadLocals) {
if (name == null) {
throw new NullPointerException("name cannot be null");
}
this.name = name;
Thread parent = currentThread();
SecurityManager security = System.getSecurityManager();
if (g == null) {
/* Determine if it's an applet or not */
/* If there is a security manager, ask the security manager
what to do. */
if (security != null) {
g = security.getThreadGroup();
}
/* If the security doesn't have a strong opinion of the matter
use the parent thread group. */
if (g == null) {
g = parent.getThreadGroup();
}
}
/* checkAccess regardless of whether or not threadgroup is
explicitly passed in. */
g.checkAccess();
/*
* Do we have the required permissions?
*/
if (security != null) {
if (isCCLOverridden(getClass())) {
security.checkPermission(SUBCLASS_IMPLEMENTATION_PERMISSION);
}
}
g.addUnstarted();
this.group = g;
this.daemon = parent.isDaemon();
this.priority = parent.getPriority();
if (security == null || isCCLOverridden(parent.getClass()))
this.contextClassLoader = parent.getContextClassLoader();
else
this.contextClassLoader = parent.contextClassLoader;
this.inheritedAccessControlContext =
acc != null ? acc : AccessController.getContext();
this.target = target;
setPriority(priority);
if (inheritThreadLocals && parent.inheritableThreadLocals != null)
this.inheritableThreadLocals =
ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);
/* Stash the specified stack size in case the VM cares */
this.stackSize = stackSize;
/* Set thread ID */
tid = nextThreadID();
}
}
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) {
/* do nothing. If start0 threw a Throwable then
it will be passed up the call stack */
}
}
}
private native void start0();
public final void setPriority(int newPriority) {
ThreadGroup g;
checkAccess();
if (newPriority > MAX_PRIORITY || newPriority < MIN_PRIORITY) {
throw new IllegalArgumentException();
}
if((g = getThreadGroup()) != null) {
if (newPriority > g.getMaxPriority()) {
newPriority = g.getMaxPriority();
}
setPriority0(priority = newPriority);
}
}
- 面試問題:呼叫start()方法和直接呼叫run()方法有什麼不同?
-
首先看一下JDK文件中關於start的介紹:
public void start()導致此執行緒開始執行; Java虛擬機器呼叫此執行緒的run方法。
結果是兩個執行緒同時執行:當前執行緒(從呼叫返回到start方法)和另一個執行緒(執行其run方法)。不止一次啟動執行緒是不合法的。 特別地,一旦執行緒完成執行,可能不會重啟執行緒。
異常
IllegalThreadStateException - 如果執行緒已經啟動。 -
看一下run()方法的文件
如果這個執行緒使用單獨的Runnable執行物件構造,那麼這個Runnable物件的run方法被呼叫; 否則,此方法不執行任何操作並返回。
Thread子類應該覆蓋此方法。
-
- 在官方文件中我們可以看出來首先第一點不同是,start方法呼叫後會真正意義上建立出一個執行緒並在記憶體中執行,而呼叫run方法就像是呼叫普通方法一樣,執行緒數量不會發生改變,並不會建立一個新的執行緒。
- 呼叫次數的限制,start方法只能呼叫一次,而run方法可以呼叫多次。這種區別主要是因為執行緒狀態的原因。要想start方法能夠正確執行,那執行緒的狀態必須是NEW狀態,而呼叫了start方法之後,執行緒會變為就緒態,只有當該新建的執行緒真正獲得CPU時執行緒狀態才會轉為RUNNABLE,此時threadStatus!=0了,所以如果再次呼叫start方法會報錯。而對於run方法則不會使執行緒的狀態發生改變,呼叫時也沒有執行緒狀態必須是NEW的要求。只有當run方法執行結束或者呼叫終止執行緒方法或執行緒異常終止時,執行緒的狀態會變為TERMINATED。
終止執行緒
首先小夥伴們可能想到stop()方法,大家也都知道stop()方法已經過時了,並且在未來Java可能會移除掉這個方法。
主要是因為這個方法太過暴力,它不會管執行緒是否執行完成就強行終止執行緒,而且會釋放掉該執行緒所持有的鎖,而鎖正是保持資料一致性所需要的。這就有可能導致資料寫到一半,執行緒終止後其他執行緒就會讀到這個被寫壞的資料。
如圖:
那麼如何安全的終止一個執行緒呢?可以通過一個標誌位來進行終止執行緒或者使用中斷來停止執行緒。
對於使用一個標誌位來實現執行緒終止程式碼實現如下:
通過自己設定標誌位或者中斷的方式停止執行緒可以有效的解決資料不一致問題和合理的清理資源。
public class MyThread extends Thread{
private volatile boolean stopMe = false;
public void stopMe(){
stopMe = true;
}
@Override
public void run(){
while(stopMe && !Thread.currentThread.isInterrupted()){
//...邏輯
}
}
}
相關文章
- Thread執行緒終止interruptthread執行緒
- Java之 join 等待執行緒終止Java執行緒
- 終止java執行緒的2種方法Java執行緒
- java多執行緒之執行緒的基本使用Java執行緒
- 如何"優雅"地終止一個執行緒?執行緒
- 併發程式設計——如何終止執行緒程式設計執行緒
- 執行緒的基本解析執行緒
- 執行緒的基本使用執行緒
- Java實現終止執行緒池中正在執行的定時任務Java執行緒
- 多執行緒核心技術(1)-執行緒的基本方法執行緒
- 【java】【多執行緒】程式、執行緒的基本概念(1)Java執行緒
- Thinking in Java---如何正確的終止子執行緒ThinkingJava執行緒
- 多執行緒------執行緒與程式/執行緒排程/建立執行緒執行緒
- 多執行緒操作執行緒
- 多執行緒的執行緒狀態及相關操作執行緒
- 執行緒的基本概念執行緒
- 執行緒的狀態轉換以及基本操作執行緒
- ObjC 多執行緒簡析(一)-多執行緒簡述和執行緒鎖的基本應用OBJ執行緒
- 執行緒基本概念執行緒
- 執行緒和程式基礎以及多執行緒的基本使用(iOS)執行緒iOS
- java--執行緒池--建立執行緒池的幾種方式與執行緒池操作詳解Java執行緒
- 執行緒和執行緒池執行緒
- 多執行緒--執行緒管理執行緒
- 執行緒與多執行緒執行緒
- 執行緒 執行緒池 Task執行緒
- 多執行緒【執行緒池】執行緒
- 執行緒、開啟執行緒的兩種方式、執行緒下的Join方法、守護執行緒執行緒
- pyqt5 子執行緒如何操作主執行緒GUIQT執行緒GUI
- Java 執行緒常用操作Java執行緒
- 保證執行緒在主執行緒執行執行緒
- 執行緒池的基本概念執行緒
- Java多執行緒-執行緒中止Java執行緒
- 多執行緒之初識執行緒執行緒
- 執行緒控制之休眠執行緒執行緒
- 【多執行緒總結(二)-執行緒安全與執行緒同步】執行緒
- 執行緒基本知識點執行緒
- 多執行緒基本概念執行緒
- Java執行緒篇——執行緒的開啟Java執行緒