1. 程式人生 > >Java之多執行緒的常用操作

Java之多執行緒的常用操作

執行緒命名與取得

在Thread類中提供有如下的執行緒名稱方法:
在這裡插入圖片描述
在Thread類中還提供有一個方法取得當前執行緒物件:

public static native Thread currentThread();

範例:設定和取得執行緒的名稱

class MyThread implements Runnable{
    @Override
    public void run() {
       for(int i=0;i<2;i++){
           System.out.println(Thread.currentThread().getName()+" "+i);
       }
    }
}
class Test{
    public static void main(String[] args){
     MyThread myThread=new MyThread();

     //直接通過物件呼叫run()方法
     new Thread(myThread).run();
     
     //通過執行緒呼叫run()方法
    Thread thread=new Thread(myThread,"執行緒1");//構造方法設定名字
    thread.start();
    thread.setName("A");//普通方法設定名字
    System.out.println(thread.getName());
    new Thread(myThread).start();//沒有設定名字
    new Thread(myThread).start();//沒有設定名字
    }
}

執行結果:
在這裡插入圖片描述
觀察上面程式碼,總結:
1.如果沒有設定執行緒名字,則會自動分配一個執行緒名字,執行緒啟動之後也可以改變執行緒的名字
2.主方法本身就是一個執行緒,所以執行緒都是通過主執行緒建立並啟動的。

執行緒休眠(sleep())

執行緒休眠:指的是讓執行緒暫緩執行一下,等到了預計時間之後再恢復執行.執行緒休眠會交出CPU,讓CPU去執行其他的任務。但是有一點要非常注意,sleep方法不會釋放鎖,也就是說如果當前執行緒持有對某個物件的鎖,則即使呼叫sleep方法,其他執行緒也無法訪問這個物件。
從執行態到阻塞態,休眠結束後從阻塞狀態到執行狀態
方法:

public static native void sleep(long millis) throws InterruptedException

休眠時間使用毫秒作為單位。
範例:實現休眠操作

class MyThread implements Runnable{
    @Override
    public void run() {
       for(int i=0;i<10;i++){
           try {
               Thread.sleep(1000);
           } catch (InterruptedException e) {
               e.printStackTrace();
           }
           System.out.println(Thread.currentThread().getName()+" "+i);
       }
    }
}
class Test{
    public static void main(String[] args){
     MyThread myThread=new MyThread();
    new Thread(myThread).start();
    new Thread(myThread).start();
    }
}

執行結果:一個執行緒不會連續列印
在這裡插入圖片描述

執行緒讓步(yield())

暫停當前正在執行的執行緒物件,並執行其他執行緒。意思就是呼叫yield方法會讓當前執行緒交出CPU許可權,讓CPU去執行其他的執行緒。它跟sleep方法類似,同樣不會釋放鎖。但是yield不能控制具體的交出CPU的時間,另外,yield方法只能讓擁有相同優先順序的執行緒有獲取CPU執行時間的機會從執行狀態返回到就緒狀態
範例:使用執行緒讓步

class MyThread implements Runnable{
    @Override
    public void run() {
       for(int i=0;i<5;i++){
           Thread.yield();
           System.out.println(Thread.currentThread().getName()+" "+i);
       }
    }
}
class Test{
    public static void main(String[] args){
     MyThread myThread=new MyThread();
    new Thread(myThread).start();
    new Thread(myThread).start();
    }
}

執行結果:一個執行緒會連續列印
在這裡插入圖片描述
注意,呼叫yield方法並不會讓執行緒進入阻塞狀態,而是讓執行緒重回就緒狀態,它只需要等待重新獲取CPU執行時間,會立馬交出CPU,這一點是和sleep方法不一樣的。

等待其他方法終止( join())

等待其他執行緒終止。意思就是如果在主執行緒中呼叫該方法時就會讓主執行緒休眠,讓呼叫該方法的執行緒run方法先執行完畢之後在開始執行主執行緒。
從執行態到阻塞態,會釋放物件鎖,其他方法執行完畢之後從阻塞態到就緒態
join()方法只是對Object類提供的wai()方法做了一層包裝而已。
範例:使用join()方法

import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;

class MyThread implements Runnable{
    @Override
    public void run() {
        System.out.println("主執行緒休眠。。。。");
        Test.printTime();
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("主執行緒休眠結束。。。。");
        Test.printTime();
    }
}
class Test{
    public static void main(String[] args){
        System.out.println("主執行緒開始。。。。");
        MyThread myThread=new MyThread();
        Thread thread=new Thread(myThread,"子執行緒");
        thread.start();
        try {
            thread.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("主執行緒結束。。。。");
    }
    public static void printTime(){
        Date date=new Date();
        DateFormat dateFormat=new SimpleDateFormat("yyy-MM-dd HH:mm:ss");
        String str=dateFormat.format(date);
        System.out.println(str);
    }
}

執行結果:
在這裡插入圖片描述

執行緒停止

多執行緒中有三種方式可以停止執行緒。

設定標記位,可以是執行緒正常退出。

範例:使用標記位使執行緒退出

class MyThread implements Runnable{
    private boolean flag=true;
    @Override
    public void run() {
        int i=1;
       while(flag){
           System.out.println(Thread.currentThread().getName()+"第"+i+"次執行");
           try {
               Thread.sleep(1000);
           } catch (InterruptedException e) {
               e.printStackTrace();
           }
           i++;
       }
    }
    public void setFlag(boolean flag) {
        this.flag = flag;
    }
}
class Test{
    public static void main(String[] args){
        MyThread myThread=new MyThread();
        Thread thread=new Thread(myThread,"A");
        thread.start();
        //主執行緒睡5秒鐘
        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        //設定子執行緒flag為false,結束執行緒
        myThread.setFlag(false);
    }
}

執行結果:
在這裡插入圖片描述

stop()

呼叫Thread類的stop方法強制使執行緒退出,但是該方法不太安全所以已經被廢棄了。
**為什麼說不安全呢?**因為stop會解除由執行緒獲取的所有鎖定,當在一個執行緒物件上呼叫stop()方法時,這個執行緒物件所執行的執行緒就會立即停止,假如一個執行緒正在執行:synchronized void { x = 3; y = 4;} 由於方法是同步的,多個執行緒訪問時總能保證x,y被同時賦值,而如果一個執行緒正在執行到x = 3;時,被呼叫了 stop()方法,即使在同步塊中,它也會馬上stop了,這樣就產生了不完整的殘廢資料。

interrupt()

使用Thread類中的一個interrupt() 可以中斷執行緒。 interrupt()只是將執行緒狀態設定為中斷狀態而已,他不會中斷一個正在執行的執行緒。此方法只是給執行緒傳遞一箇中斷訊號,程式可以根據此訊號來判斷是否需要終止。
範例:

class MyThread implements Runnable{

    @Override
    public void run() {
        int i=1;
       while(true){
       //判斷執行緒是否被中斷
           boolean bool=Thread.currentThread().isInterrupted();
           System.out.println(Thread.currentThread().getName()+"第"+i+"次執行");
           System.out.println(bool);
           //如果執行緒中斷,手動退出執行緒(如果不手動退出,系統可能不會是執行緒退出)
           if(bool){
               System.out.println("執行緒退出");
               break;
           }
           i++;
       }
    }
}
class Test{
    public static void main(String[] args){
        MyThread myThread=new MyThread();
        Thread thread=new Thread(myThread,"A");
        thread.start();
        try {
            Thread.sleep(100);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        thread.interrupt();
    }
}

執行結果:interrupt() 方法只是改變中斷狀態而已,它不會中斷一個正在執行的執行緒。
在這裡插入圖片描述

當執行緒中使用了wait、sleep、join方法導致此執行緒阻塞,則interrupt()會線上程中丟擲InterruptException,並且將執行緒的中斷狀態由true置為false。
範例:

class MyThread implements Runnable{

    @Override
    public void run() {
        int i=1;
       while(true){
           try {
               Thread.sleep(1000);
               boolean bool=Thread.currentThread().isInterrupted();
               if(bool){
                   System.out.println("執行緒退出");
                   break;
               }
               System.out.println(Thread.currentThread().getName()+"第"+i+"次執行");
               System.out.println(bool);
               i++;
           } catch (InterruptedException e) {
               System.out.println("異常丟擲,執行緒停止");
               boolean bool=Thread.currentThread().isInterrupted();
               System.out.println("Catch塊中中斷為"+bool);
               return ;
           }
       }
    }
}
class Test{
    public static void main(String[] args)throws InterruptedException {
        MyThread myThread=new MyThread();
        Thread thread=new Thread(myThread,"A");
        thread.start();
        Thread.sleep(3000);

        thread.interrupt();
    }
}

執行結果:
在這裡插入圖片描述

執行緒優先順序

執行緒優先順序是指優先順序越高就有可能先執行,但是僅僅是有可能而已!!!

  • 設定優先順序
  public final void setPriority(int newPriority);
  • 取得優先順序
public final int getPriority();

對於優先順序設定的內容可以通過Thread類的幾個常量來決定

1. 最高優先順序:public final static int MAX_PRIORITY = 10;
2. 中等優先順序:public final static int NORM_PRIORITY = 5;
3. 最低優先順序:public final static int MIN_PRIORITY = 1;

範例:優先順序的設定與獲取

class MyThread implements Runnable{

    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName()+"執行緒正在執行");
    }
}
class Test{
    public static void main(String[] args)throws InterruptedException {
        MyThread myThread=new MyThread();
        //得到主方法的執行緒優先順序
        System.out.println(Thread.currentThread().getPriority());
        Thread thread1=new Thread(myThread,"A");
        Thread thread2=new Thread(myThread,"B");
        Thread thread3=new Thread(myThread,"C");
        thread1.setPriority(Thread.MIN_PRIORITY);
        thread2.setPriority(Thread.NORM_PRIORITY);
        thread3.setPriority(Thread.MAX_PRIORITY);
        System.out.println("A的執行緒優先順序"+thread1.getPriority());
        System.out.println("B的執行緒優先順序"+thread2.getPriority());
        System.out.println("C的執行緒優先順序"+thread3.getPriority());
        thread1.start();
        thread2.start();
        thread3.start();
    }
}

執行結果:
在這裡插入圖片描述
觀察上述程式碼的執行結果,我們發現主執行緒只是一個普通中等優先順序而已,最主要的是,雖然設計了A、B、C執行緒的優先順序從小到大但是執行緒在執行的時候並沒有按照這個優先順序的順序來執行。
執行緒具有繼承性:只是繼承優先順序而已(從A執行緒啟動B執行緒,則B和A的執行緒優先順序是一樣的)
範例:觀察執行緒的繼承性

class A implements Runnable{

    @Override
    public void run() {
        System.out.println("A的優先順序"+Thread.currentThread().getPriority());
        Thread thread=new Thread(new B());
        thread.start();
    }
}
class B implements Runnable{
    @Override
    public void run() {
        System.out.println("B的優先順序"+Thread.currentThread().getPriority());
    }
}
class Test{
    public static void main(String[] args)throws InterruptedException {
       A a=new A();
       Thread thread=new Thread(a);
       thread.setPriority(Thread.MAX_PRIORITY);
       thread.start();
    }
}

執行結果:在沒有設定執行緒A的優先順序之前,A和B的優先順序都是5,因為A繼承了主執行緒的優先順序,B又繼承了A的優先順序
在這裡插入圖片描述

守護執行緒

守護執行緒是一種特殊的執行緒,又稱為一種陪伴執行緒。Java中共分為兩種執行緒:使用者執行緒和守護執行緒。
Thread類中提供了isDaemon()區別兩種執行緒:返回false,表示該執行緒為使用者執行緒返回true就是守護執行緒典型守護執行緒就是垃圾回收執行緒。
只要當前JVM程序中存在任何一個使用者執行緒沒有結束,守護執行緒就在工作;只有當最後一個使用者執行緒結束時,守護執行緒才會隨著JVM一同停止工作。
Thread提供**setDaemon()**將使用者執行緒設定成為守護執行緒。
注意:主執行緒main是使用者執行緒
範例:

class A implements Runnable{
   private int i;
    @Override
    public void run() {
        try{
        while (true) {
            i++;
            System.out.println("執行緒名稱:" + Thread.currentThread().getName() + ",i=" + i +
                    ",是否為守護執行緒:"
                    + Thread.currentThread().isDaemon());
            Thread.sleep(1000);
        }
    } catch (InterruptedException e) {
        System.out.println("執行緒名稱:" + Thread.currentThread().getName() + "中斷執行緒了");
    }
    }
}

class Test{
    public static void main(String[] args)throws InterruptedException {
        Thread thread1 = new Thread(new A(),"子執行緒A");
         // 設定執行緒A為守護執行緒,此語句必須在start方法之前執行
       thread1.setDaemon(true);
        thread1.start();

        Thread thread2 = new Thread(new A(),"子執行緒B");
        thread2.start();
        Thread.sleep(3000);
        // 中斷非守護執行緒,非守護執行緒結束
        thread2.interrupt();


        Thread.sleep(10000);
        System.out.println("程式碼結束");

    }
}

執行結果:
在這裡插入圖片描述
從上面的程式碼可以看出來,B是使用者執行緒當它中斷了之後守護執行緒還沒有結束,是因為主執行緒(使用者執行緒)還沒有結束,所以說明是所有的使用者執行緒結束之後守護執行緒才會結束。