1. 程式人生 > >深入理解Java多執行緒(一)

深入理解Java多執行緒(一)

關於java多執行緒的概念以及基本用法:java多執行緒基礎

1,停止執行緒

停止執行緒意味著線上程執行完之前停止正在做的操作,即立刻放棄當前的操作,這並不容易。停止執行緒可以用Thread.stop()方法,但是這個方法不安全,所以不建議使用,還有一個方法就是Thread.interrupt()方法,但是這個方法不會終止一個正在執行的執行緒,需要新增一個判斷才可以完成執行緒的停止

1.1,停不下來的執行緒

  • public void interrupt()
    將呼叫者執行緒的中斷狀態設為true

Thread.interrupt()方法只是在當前執行緒中打了一個停止的標記,並不是直接將執行緒停止

class MyThread_1 extends Thread{

    @Override
    public void run() {
        super.run();
        for(int i=0;i<10000;i++) {
            System.out.println("i="+(i+1));
        }
    }

}

public class Demo1_7_1 {
    public static void main(String[] args) {
        try {
            MyThread_1 thread = new
MyThread_1(); thread.start(); Thread.sleep(200); thread.interrupt(); } catch (Exception e) { e.printStackTrace(); } } }

結果:
這裡寫圖片描述
從結果可以看到,使用thread.interrupt()執行緒並未停止

1.2,判斷執行緒是否是停止狀態

  • public static boolean interrupted
    只能通過Thread.interrupted()呼叫
class MyThread_2 extends Thread{
    @Override
    public void run() {
        super.run();
        for(int i=0;i<10000;i++) {
            System.out.println("i="+(i+1));
        }
    }
}

public class Demo1_7_2 {
    public static void main(String[] args) {
        try {
            MyThread_2 thread = new  MyThread_2();
            thread.start();
            Thread.sleep(2000);
            thread.interrupt();
            System.out.println("是否停止1?="+thread.interrupted());
            System.out.println("是否停止2?="+thread.interrupted());
        } catch (Exception e) {
            e.printStackTrace();
        }
        System.out.println("end");
    }
}

執行結果:
這裡寫圖片描述
當前執行緒是main並沒有中斷過,所以列印結果都是false

將main函式程式碼改為

        Thread.currentThread().interrupt();
        System.out.println("是否停止1?="+Thread.interrupted());
        System.out.println("是否停止2?="+Thread.interrupted());
        System.out.println("end");

結果為

是否停止1?=true
是否停止2?=false
end

為什麼第二個值是false?這是因為第一次呼叫Thread.interrupted()返回當前執行緒中斷狀態,然後會將執行緒的中斷狀態設為false

  • public boolean isInterrupted()
    判斷呼叫者執行緒的中斷狀態

講main函式修改

        try {
        MyThread_2 thread = new  MyThread_2();
        thread.start();
        Thread.sleep(2000);
        thread.interrupt();
        System.out.println("是否停止1?="+thread.isInterrupted());
        System.out.println("是否停止2?="+thread.isInterrupted());
    } catch (Exception e) {
        e.printStackTrace();
    }
    System.out.println("end");

執行結果是:

。。。
。。。。
i=9997
i=9998
i=9999
i=10000
是否停止1?=false
是否停止2?=false
end

很神奇這裡返回結果都是false,想了半天,後來在網上查了資料明白是sleep函式的原因Thread.sleep(2000);休眠的是主執行緒,這裡休眠的就是main函式,在這段時間裡thread可能已經執行完了,所以thread.interrupt();也就起不到作用了,修改sleep函式時間,返回結果就都是true了

此方法作用:測試執行緒是否已經是中斷狀態,但是不會執行一次後將中斷狀態設定為false

1.3,停止執行緒–異常法

使用if語句判斷執行緒是否是停止狀態來控制後面的程式碼是否繼續執行,但是無論是否停止執行緒,for迴圈外run方法裡的程式碼都會執行,這裡用throw new InterruptedException();來產生異常,從而結束執行緒,執行緒其餘程式碼也不會執行了

class MyThread_3 extends Thread{

    @Override
    public void run() {
        super.run();
        try {
        for(int i=0;i<500000;i++) {
            if(this.isInterrupted()) {
            System.out.println("已經是停止狀態了,我要退出");
            throw new InterruptedException();

            }
            System.out.println("i="+(i+1));
        }
        System.out.println("for下面");
        }catch(InterruptedException e) {
            System.out.println("進Mythread類run方法中的catch");
            e.printStackTrace();
        }

    }
}

public class Demo1_7_3 {
    public static void main(String[] args) {
        try {
            MyThread_3 thread = new MyThread_3();
            thread.start();
            Thread.sleep(2000);
            thread.interrupt();
        } catch (Exception e) {
            e.printStackTrace();
        }
        System.out.println("end");
    }
}

結果:

。。。。
。。。。。
i=366068
i=366069
i=366070
end
已經是停止狀態了,我要退出
進Mythread類run方法中的catch
java.lang.InterruptedException
    at chapter_1.MyThread_3.run(Demo1_7_3.java:12)

1.4,停止執行緒–在Sleep狀態

在sleep狀態下停止某一執行緒會進入catch語句,並且清除停止狀態值,使之變成false

class MyThread_4 extends Thread{

    @Override
    public void run() {
        super.run();
        try {
            System.out.println("run begin");
            Thread.sleep(200000);
            System.out.println("run end");
        } catch (Exception e) {
            // TODO: handle exception
            System.out.println("在沉睡中被停止進入catch"+this.isInterrupted());
            e.printStackTrace();
        }
    }
}

public class Demo1_7_4 {
    public static void main(String[] args) {
        try {
            MyThread_4 thread = new MyThread_4();
            thread.start();
            Thread.sleep(200);
            thread.interrupt();
        } catch (Exception e) {
            // TODO: handle exception
            System.out.println("main catch");
            e.printStackTrace();
        }
        System.out.println("end");
    }

}

結果:

run begin
end
在沉睡中被停止進入catchfalse
java.lang.InterruptedException: sleep interrupted
    at java.lang.Thread.sleep(Native Method)
    at chapter_1.MyThread_4.run(Demo1_7_4.java:10)

1.5,停止執行緒–暴力停止

所謂暴力停止就是最直接的方法了–stop(),這種方法直接讓執行緒停止,但是這也會引來一些問題,比如執行緒的安全問題,不建議使用

1.6,停止執行緒–使用return

將方法interrupt()與return結合使用也可以實現停止執行緒

class MyThread_8 extends Thread{

    @Override
    public void run() {
        super.run();
        while(true) {
            if(this.isInterrupted()) {
                System.out.println("停止了!");
                return;
            }
            System.out.println("timer="+System.currentTimeMillis());
        }
    }
}

public class Demo1_7_8 {
    public static void main(String[] args) throws InterruptedException{
        MyThread_8 t = new MyThread_8();
        t.start();
        Thread.sleep(2000);
        t.interrupt();
    }
}

結果:

。。。
。。。
timer=1533907137644
timer=1533907137644
timer=1533907137644
timer=1533907137644
timer=1533907137644
timer=1533907137644
停止了!

1.7,yield方法

yield()方法作用是放棄當前CPU資源,讓給其他執行緒去使用,但是放棄時間不確定

1.8,執行緒優先順序

作業系統會對執行緒進行劃分優先順序,優先順序高的執行緒會優先分配到資源,類似於現實中的VIP,Java將執行緒從1到10劃分十個等級,預設優先順序為NORM_PRIORITY,設定優先順序用setPriority()
setPriority()原始碼:

    public final void setPriority(int i)
    {
        checkAccess();
        if(i > 10 || i < 1)
            throw new IllegalArgumentException();
        ThreadGroup threadgroup;
        if((threadgroup = getThreadGroup()) != null)
        {
            if(i > threadgroup.getMaxPriority())
                i = threadgroup.getMaxPriority();
            setPriority0(priority = i);
        }
    }
  • public final static int MIN_PRIORITY=1;
  • public final static int NORM_PRIORITY=5;
  • public final static int MAX_PRIORITY=10;

此外執行緒還具有繼承性,若執行緒A啟動執行緒B,則B具有A的優先順序

1.9,守護執行緒

在Java執行緒中有兩種執行緒,一種是使用者執行緒一種是守護執行緒,我們常說的作業系統中並沒有守護執行緒這一說,原因是Java是構建在JVM之上的。顧名思義,守護執行緒具有陪伴的意思,當程序中不存在非守護執行緒時,守護執行緒就會自動銷燬。
Daemon作用是為了其他執行緒的執行提供便利服務,只有當最後一個非守護執行緒結束時,守護執行緒才會隨著JVM一同結束工作,守護執行緒典型的例子就是GC

class MyThread_11 extends Thread{
    private int i=0;
    @Override
    public void run() {
        super.run();
        try {
            while(true) {
                i++;
                System.out.println("i="+(i));
                Thread.sleep(1000);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

}

public class Demo1_11 {

    public static void main(String[] args) {
        try {
            MyThread_11 thread = new MyThread_11();
            thread.setDaemon(true);
            thread.start();
            Thread.sleep(5000);
            System.out.println("守護執行緒離開了!");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

結果:

i=1
i=2
i=3
i=4
i=5
守護執行緒離開了!

2018.9.9補充:
突然對this與Thread.currentThread()產生了疑問,下面來對這個區別來介紹一下:
建立一個MyObject類:

public class MyThread extends Thread{

    public MyThread() {
    }

    @Override
    public void run() {

        System.out.println("Thread.currentThread.getName= "
        +Thread.currentThread().getName());
        System.out.println("this.getName= "+this.getName());
    }

}

測試類:

public class Run {

    public static void main(String[] args) {
        MyThread myThread = new MyThread();
        Thread t = new Thread(myThread);
        t.start();
    }
}

結果:

Thread.currentThread.getName= Thread-1
this.getName= Thread-0

顯然這裡this和Thread.currentThread()指向的不是同一個執行緒,看一下Thread原始碼:
這裡寫圖片描述

Thread t = new Thread(myThread)這句傳入的物件MyThread就是原始碼裡的target物件,Thread類的構造方法會給執行緒一個預設的名字,從”Thread-0”開始
this.getName()其實就是target.getName(),返回的是執行MyThread myThread = new MyThread()的執行緒名,而Thread.currentThread().getName()返回的是t.getName(),返回的是Thread t = new Thread(myThread)這句建立的執行緒