Java入門教程十三(多執行緒)

韭菜Java發表於2019-06-11

執行緒的概念

單純種以一個任務完成以後再進行下一個任務的模式進行,這樣下一個任務的開始必須等待前一個任務的結束,只有一個任務完成後才能進行下一個任務。Java 語言提供了併發機制,允許開發人員在程式中執行多個執行緒,每個執行緒完成一個功能,並與其他執行緒併發執行。這種機制被稱為多執行緒。作業系統以程式為單位,我們下Windows

Java入門教程十三(多執行緒)

系統可以分配給每個程式一段有限的執行 CPU 的時間(也稱為 CPU 時間片),CPU 在這段時間中執行某個程式,然後下一個時間段又跳到另一個程式中去執行。由於 CPU 切換的速度非常快,給使用者的感受就是這些任務似乎在同時執行,所以使用多執行緒技術後,可以在同一時間內執行更多不同種類的任務。

單任務的特點就是排隊執行,也就是同步,就像在 cmd 中輸入一條命令後,必須等待這條命令執行完才可以執行下一條命令一樣。

Java入門教程十三(多執行緒)

CPU 完全可以在任務 1 和任務 2 之間來回切換,使任務 2 不必等到 5 秒再執行,系統的執行效率大大得到提升。這就是要使用多執行緒技術,執行緒可以理解成是在程式中獨立執行的子任務。

實現方式

實現多執行緒程式設計的方式主要有兩種:一種是繼承 Thread 類,另一種是實現 Runnable 介面

Thread類

public Thread(String threadName)
public Thread()

public class NewThread extends Thread{
     @Override
    public void run(){
       //執行緒的執行程式碼
    }
}
//使用
new NewThread().start();

執行緒實現的業務程式碼需要放到 run() 方法中。當一個類繼承 Thread 類後,就可以在該類中覆蓋 run() 方法,將實現執行緒功能的程式碼寫入 run() 方法中,然後同時呼叫 Thread 類的 start() 方法執行執行緒,也就是呼叫 run() 方法

Runnable介面

如果要建立的執行緒類已經有一個父類,這時就不能再繼承 Thread 類,因為 Java 不支援多繼承,所以需要實現 Runnable 介面來應對這樣的情況

public Thread(Runnable r);
public Thread(Runnable r,String name);

public class MyRunnable implements Runnable{
    @Override 
    public void run(){ 
        //執行緒的執行程式碼
    }
}
//使用
Runnable runnable=new MyRunnable();
Thread thread=new Thread(runnable);
thread.start();

執行緒的生命週期

執行緒也具有生命週期,主要包括 7 種狀態,分別是出生狀態、就緒狀態、執行狀態、等待狀態、休眠狀態、阻塞狀態和死亡狀態

Java入門教程十三(多執行緒)

出生狀態:使用者在建立執行緒時所處的狀態,在使用者使用該執行緒例項呼叫 start() 方法之前,執行緒都處於出生狀態。

就緒狀態:也稱可執行狀態,當使用者呼叫 start() 方法之後,執行緒處於就緒狀態。

執行狀態:當執行緒得到系統資源後進入執行狀態。

等待狀態:當處於執行狀態下的執行緒呼叫 Thread 類的 wait() 方法時,該執行緒就會進入等待狀態。進入等待狀態的執行緒必

須呼叫 Thread 類的 notify() 方法才能被喚醒。notifyAll() 方法是將所有處於等待狀態下的執行緒喚醒。

休眠狀態:當執行緒呼叫 Thread 類中的 sleep() 方法時,則會進入休眠狀態。

阻塞狀態:如果一個執行緒在執行狀態下發出輸入/輸出請求,該執行緒將進入阻塞狀態,在其等待輸入/輸出結束時,執行緒進入就緒狀態。對阻塞的執行緒來說,即使系統資源關閉,執行緒依然不能回到執行狀態。

死亡狀態:當執行緒的 run() 方法執行完畢,執行緒進入死亡狀態。

同步機制 synchronized

為了處理這種共享資源競爭,可以使用同步機制。所謂同步機制,指的是兩個執行緒同時作用在一個物件上,應該保持物件資料的統一性和整體性。Java 提供 synchronized 關鍵字,為防止資源衝突提供了內建支援。

同步方法

class className{
    public synchronized type methodName(){
        //程式碼
    }
}

同步塊

synchronized(obj){
    //程式碼
}
public class test{
    Object obj=new Object();
    public void method(){
        synchronized(obj){
            //程式碼
        }
    }
}

同步的多執行緒與單執行緒有本質的區別,當處理一段非常複雜的業務時,使用了多執行緒處理,只有被synchronized的程式碼塊才會被進行順序執行,其他的業務程式碼都是在不同的執行緒裡各自執行。

curentThread()

返回程式碼段正在被哪個執行緒呼叫的執行緒相關資訊

public static void main(String[] args)
{
   //呼叫currentThread()方法輸出當前執行緒名稱
   System.out.println(Thread.currentThread().getName());
}
public class MyThread extends Thread{
    @Override 
   public void run(){
        System.out.println(Thread.currentThread().getName()); 
   }
}
MyThread myThread=new MyThread();
Thread t = new Thread(myThread);
t.setName("myThread");
t.start();//輸出myThread;

isAlive()

isAlive() 方法的作用是判斷當前的執行緒是否處於活動狀態。什麼是活動狀態呢?活動狀態就是執行緒已經啟動且尚未終止。執行緒處於正在執行或準備開始執行的狀態,就認為執行緒是“存活”的

public class MyThread extends Thread
{
    @Override 
    public void run()
    { 
        System.out.println("run="+this.isAlive()); 
    }
}
public static void main(String[] args)
{
    MyThread mythread=new MyThread(); 
    System.out.println("begin="+mythread.isAlive());   //輸出執行緒狀態
    mythread.start();                                  //啟動執行緒
    System.out.println("end="+mythread.isAlive());     //輸出執行緒狀態
}
//輸出
begin==false
end==true或false//這裡要注意,由於另啟一個執行緒,去執行程式碼,end有可能線上程啟動前執行(此情況end為false),也有可能線上程啟動後執行(此情況end為ture)
run=true

sleep()

sleep() 方法的作用是在指定的毫秒數內讓當前“正在執行的執行緒”休眠(暫停執行)

public class MyThread extends Thread
{
    @Override 
    public void run()
    { 
        System.out.println("開始"); 
        Thread.sleep(2000);        //延時2秒
        System.out.println("結束"); 
    }
}

getId()

getId() 取得正在執行執行緒的唯一標識

Thread thread=Thread.currentThread();
System.out.println(thread.getId());

執行緒暫停

暫停執行緒意味著此執行緒還可以恢復執行。使用 suspend() 方法暫停執行緒,使用 resume() 方法恢復執行緒的執行

public class MyThread extends Thread
{
    private long i=0;
    public long getI()
    {
        return i;
    }
    public void setI(long i)
    {
        this.i=i;
    }
    @Override
    public void run()
    {
        while(true)
        {
            i++;
        }
    }
}
public static void main(String[] args){
     MyThread thread=new MyThread();
     thread.start();
     System.out.println("執行緒開始");
     System.out.println(System.currentTimeMillis()+" i= "+thread.getI());//輸出i的值
     thread.suspend();//暫停
     System.out.println("執行緒暫停5秒");
     Thread.sleep(5000);
     thread.resume();//開始
     System.out.println(System.currentTimeMillis()+" i= "+thread.getI());//繼續暫停後i的值
}       

注意:在使用 suspend() 方法與 resume() 方法時,如果使用不當極易造成公共的同步物件被獨佔,從而使得其他執行緒無法訪問公共同步物件。使用時要格外注意。

執行緒停止

停止一個執行緒意味著線上程處理完任務之前停掉正在做的操作,也就是放棄當前的操作。有三種方法可以停止執行緒

使用退出標識,使執行緒正常退出,也就是當 run() 方法完成後執行緒終止。
使用 stop() 方法強行終止執行緒,但是不推薦使用這個方法,因為 stop() 和 suspend() 及 resume() 一樣,都是作廢過期的方法,使用它們可能產生不可預料的結果。
使用 interrupt() 方法中斷執行緒。

interrupt()

interrupt() 方法的作用是用來停止執行緒,但 intermpt() 方法的使用效果並不像迴圈結構中 break 語句那樣,可以馬上停止迴圈。呼叫 intermpt() 方法僅僅是在當前執行緒中打了一個停止的標記,並不是真的停止執行緒

public class MyThread extends Thread
{
    @Override 
    public void run()
    { 
        for (int i=0;i<10000;i++)
        { 
            System.out.println(i+1);
        } 
    } 
}

public static void main(String[] args){ 
    MyThread thread=new MyThread();      //建立MyThread13執行緒類例項
    thread.start();    //啟動執行緒
    Thread.sleep(100);    //延時100毫秒
    thread.interrupt();    //停止執行緒
}

主執行緒的執行結果如下所示。從中可以看到,雖然在延時 100 毫秒後呼叫 intermpt() 方法停止了 thread 執行緒,但是該執行緒仍然執行完成輸出 10000 行資訊。

判斷執行緒是不是停止狀態

this.interrupted():測試當前執行緒是否已經中斷。
this.islnterrupted():測試執行緒是否已經中斷。

stop()

呼叫 stop() 方法可以在任意情況下強制停止一個執行緒

public class MyThread extends Thread{
   private int i=0;
   @Override 
   public void run(){ 
            while (true){
                i++;
                System.out.println("i=" + i);
                Thread.sleep(1000);
        }
    } 
}
MyThread thread=new MyThread();
thread.start();
Thread.sleep(8000);
thread.stop();

執行緒在啟動後有一個 8000 毫秒的延時,在這段時間內會迴圈 9 次,之後 stop() 方法被執行從而執行緒停止。執行後輸出1到9

呼叫 stop() 方法時會丟擲 java.lang.ThreadDeath 異常,但在通常情況下,此異常不需要顯式地捕捉。使用 stop() 釋放鎖將會給資料造成不一致性的結果。如果出現這樣的情況,程式處理的資料就有可能遭到破壞,最終導致程式執行的流程錯誤,一定要特別注意。

相關文章