Java入門教程十三(多執行緒)
執行緒的概念
單純種以一個任務完成以後再進行下一個任務的模式進行,這樣下一個任務的開始必須等待前一個任務的結束,只有一個任務完成後才能進行下一個任務。Java 語言提供了併發機制,允許開發人員在程式中執行多個執行緒,每個執行緒完成一個功能,並與其他執行緒併發執行。這種機制被稱為多執行緒。作業系統以程序為單位,我們下Windows
系統可以分配給每個程序一段有限的執行 CPU 的時間(也稱為 CPU 時間片),CPU 在這段時間中執行某個程序,然後下一個時間段又跳到另一個程序中去執行。由於 CPU 切換的速度非常快,給使用者的感受就是這些任務似乎在同時執行,所以使用多執行緒技術後,可以在同一時間內執行更多不同種類的任務。
單任務的特點就是排隊執行,也就是同步,就像在 cmd 中輸入一條命令後,必須等待這條命令執行完才可以執行下一條命令一樣。
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 種狀態,分別是出生狀態、就緒狀態、執行狀態、等待狀態、休眠狀態、阻塞狀態和死亡狀態
出生狀態:使用者在建立執行緒時所處的狀態,在使用者使用該執行緒例項呼叫 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() 釋放鎖將會給資料造成不一致性的結果。如果出現這樣的情況,程式處理的資料就有可能遭到破壞,最終導致程式執行的流程錯誤,一定要特別注意