初識Java多線程

第一章 Java多線程技能

1)線程的啟動;
2)如何使線程暫停;
3)線程的優(yōu)先級;
4)線程安全相關(guān)的問題

1.1 進程和多線程的概念及多線程的優(yōu)點

進程:受操作系統(tǒng)管理的基本運行單元。

線程:進程中獨立運行的子任務(wù)。

多線程的優(yōu)點:
在圖1-1-1中,任務(wù)1與任務(wù)2是兩個完全獨立,互不相關(guān)的任務(wù),任務(wù)1在等待遠程服務(wù)器返回數(shù)據(jù),這時CPU一直“空運行”,必須要等到任務(wù)1執(zhí)行完畢后才執(zhí)行任務(wù)2。以上概括了單任務(wù)的特點:排隊執(zhí)行,也就是同步。這就是單任務(wù)環(huán)境的缺點,CPU利用率大幅降低。
在圖1-1-2可以發(fā)現(xiàn),CPU完全可以再任務(wù)1與任務(wù)2之間來回切換,使任務(wù)2不必等待任務(wù)1執(zhí)行完畢再進行,系統(tǒng)的運行效率大大提升。

image

1.2 使用多線程

實現(xiàn)多線程的的方式有兩種,一種是繼承Thread類,另一種是實現(xiàn)Runnable接口。

1.2.1 繼承Thread類

public class Thread implements Runnable{}

使用繼承Thread類的方式來創(chuàng)建多線程時,最大的局限性就是不支持多繼承(Java語言的特點就是單根繼承)。下面使用第一種方式,繼承Thread類。

public class MyThread extends Thread {
    @Override
    public void run() {
        super.run();
        System.out.println("MyThread");
    }
}
public class Run{
    public static void main(String[] args) {
        MyThread myThread =new MyThread();
        myThread.start();
        System.out.println("運行結(jié)束");
    }
}
運行結(jié)束
MyThread

從執(zhí)行結(jié)果來看,MyThread類中run方法執(zhí)行的時間較晚,這也說明在使用多線程技術(shù)時,代碼的運行結(jié)果與代碼的執(zhí)行順序或調(diào)用順序是無關(guān)的。線程是一個子任務(wù),CPU以不確定的方式,或者說是以隨機的時間來調(diào)用線程中的run方法,所以就會出現(xiàn)如上結(jié)果。 如果多次調(diào)用start(),則會出現(xiàn) Exception in thread "main" java.lang.IllegalThreadStateException

上面介紹了線程調(diào)用的隨機性,下面我們再演示線程的隨機性,如下。

public class MyThread extends Thread{
    @Override
    public void run() {
        try {
            for (int i = 0; i < 5; i++) {
                int time =(int) (Math.random() * 1000);
                Thread.sleep(time);
                System.out.println("run=" + Thread.currentThread().getName());
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
public class Run{
    public static void main(String[] args) {
        try {
            MyThread myThread =new MyThread();
            myThread.setName("MyThread");
            myThread.start();
            for (int i = 0; i < 5; i++) {
                int time =(int) (Math.random() * 1000);
                Thread.sleep(time);
                System.out.println("main=" + Thread.currentThread().getName());
            }
        } catch (InterruptedException e) {
            e.fillInStackTrace();
        }
    }
}

run=MyThread
main=main
main=main
run=MyThread
run=MyThread
main=main
run=MyThread
run=MyThread
main=main
main=main

繼承Thread,重寫run方法,調(diào)用start方法執(zhí)行線程的流程:調(diào)用Thread類中的start方法通知線程規(guī)劃器此線程已經(jīng)準備就緒,等待調(diào)用線程對象的run方法。這個過程其實就是讓系統(tǒng)安排一個時間來調(diào)用Thread中的run方法,使線程得到運行,線程為異步執(zhí)行;如果直接調(diào)用Thread.run(),那么此線程對象并不交給“線程規(guī)劃器”來處理,而是由main主線程來調(diào)用run(),也就是同步執(zhí)行。

1.2.2 實現(xiàn)Runnable接口

如果預創(chuàng)建的線程類已經(jīng)有一個父類了,這時候就不能再繼承Thread了,因為Java不支持多繼承,所以就需要實現(xiàn)Runnbale接口來應(yīng)對這種情況。 如何使用Runnable接口呢,那么就要看一下Thread的構(gòu)造函數(shù)了。

public Thread(){}
public Thread(Runnable target){}
public Thread(ThreadGroup group, Runnable target){}
public Thread(String name){}
public Thread(ThreadGroup group, String name){}
public Thread(Runnable target, String name){}
public Thread(ThreadGroup group, Runnable target, String name){}
public Thread(ThreadGroup group, Runnable target, String name, long stackSize){}

在Thread類的8個構(gòu)造函數(shù)中,有兩個個構(gòu)造函數(shù)

Thread(Runnable target)
Thread(Runnable target, String name)
public class MyRunnable implements Runnable{
    @Override
    public void run() {
        System.out.println("running");
    }
}
public class RunMyRunnable{
    public static void main(String[] args) {
        Thread thread  =new Thread(new MyRunnable());
        System.out.println(thread.getName());
        thread.start();
        Thread thread2  =new Thread(new MyRunnable(),"customize name");
        System.out.println(thread2.getName());
        thread2.start();
        System.out.println("運行結(jié)束");
    }
}

Thread-0
customize name
運行結(jié)束
running
running

1.2.3 實例變量與線程安全

自定義線程類中的實例變量針對其他線程有共享不共享之分,這在多線程之間進行交互時是很重要的一個技術(shù)點。

1.2.3.1 不共享數(shù)據(jù)的情況

public class MyThread extends Thread{
    private int count =5;
    public MyThread(String name) {
        this.setName(name);
    }
    @Override
    public void run() {
        super.run();
        while (count >0) {
            count--;
            System.out.println("由" +this.currentThread().getName() +"計算,count=" +count);
        }
    }
}
public class Run{
    public static void main(String[] args) {
        MyThread myThreadA =new MyThread("myThreadA");
        MyThread myThreadB =new MyThread("myThreadB");
        MyThread myThreadC =new MyThread("myThreadC");
        myThreadA.start();
        myThreadB.start();
        myThreadC.start();
    }
}
由myThreadA計算,count=4
由myThreadC計算,count=4
由myThreadC計算,count=3
由myThreadC計算,count=2
由myThreadC計算,count=1
由myThreadC計算,count=0
由myThreadA計算,count=3
由myThreadA計算,count=2
由myThreadA計算,count=1
由myThreadA計算,count=0
由myThreadB計算,count=4
由myThreadB計算,count=3
由myThreadB計算,count=2
由myThreadB計算,count=1
由myThreadB計算,count=0

1.2.3.2 共享數(shù)據(jù)的情況

共享數(shù)據(jù)是指多個線程同時訪問同一個變量。

public class MyThread extends Thread{
    private int count =5;
    @Override
    public void run() {
        super.run();
        count--;
        System.out.println("由" +this.currentThread().getName() +"計算,count=" +count);
    }
}
public class Run{
    public static void main(String[] args) {
        MyThread myThreadA =new MyThread();
        Thread a =new Thread(myThreadA,"A");
        Thread b =new Thread(myThreadA,"B");
        Thread c =new Thread(myThreadA,"C");
        Thread d =new Thread(myThreadA,"D");
        Thread e =new Thread(myThreadA,"E");
        a.start();
        b.start();
        c.start();
        d.start();
        e.start();
    }
}
由C計算,count=3
由B計算,count=3
由A計算,count=2
由E計算,count=1
由D計算,count=0

線程C和B打印出的count值都是3,說明A和B同時獲取到count并進行--的操作,產(chǎn)生了非線程安全問題。count--的操作分成三步:
1)取得原有count值;
2)計算count-1;
3)對count賦值。
如果多個線程同時訪問,一定會出現(xiàn)非線程安全問題。

那么如何解決上面的線程安全問題呢?這時就需要使多個線程之間進行同步。更改MyThread類代碼如下:

public class MyThread extends Thread{
    private int count =5;
    @Override
    synchronized public void run() {
        super.run();
        count--;
        System.out.println("由" +this.currentThread().getName() +"計算,count=" +count);
    }
}
由A計算,count=4
由E計算,count=3
由B計算,count=2
由D計算,count=1
由C計算,count=0

運行之后,執(zhí)行結(jié)果與預期一致。通過在run方法前加入synchronized,使多個線程在執(zhí)行run方法時,以排隊的方式進行處理。當一個線程調(diào)用run前,先判斷run方法有沒有被上鎖,如果上鎖,說明有其他線程正在調(diào)用run方法,必須等其他線程對run方法調(diào)用結(jié)束之后才可以執(zhí)行。這樣也就實現(xiàn)了排隊調(diào)用run方法的目的。synchronized可以在任意對象及方法上加鎖,而加鎖的這段代碼成為互斥區(qū)臨界區(qū)。

當一個線程想要執(zhí)行同步方法里面的代碼時,線程首先嘗試去拿這把鎖,如果能拿到這把鎖,那么這個線程就可以執(zhí)行synchronized里面的代碼。如果不能獲取這把鎖,那么這個線程就會不斷地嘗試拿這把鎖,直到拿到為止。

非線程安全主要是指多個線程對同一對象中的同一實例變量進行操作時,會出現(xiàn)值被更改,值不同步的情況,進而影響程序的執(zhí)行流程。

下面來實現(xiàn)一個非線程安全導致的后果。

public class LoginServlet{
    private static StringuserNameRef;
    private static StringpasswordRef;
    public static void login(String userName, String password) {
        userNameRef = userName;
        try {
            if (userName.equals("a")) {
                Thread.sleep(5000);
            }
            passwordRef = password;
            System.out.println("當前的用戶名:" +userNameRef +" 密碼:" +passwordRef);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

public class ALogin extends Thread{
    @Override
    public void run() {
        LoginServlet.login("a", "aa");
    }
}
public class BLogin extends Thread{
    @Override
    public void run() {
        LoginServlet.login("b", "bb");
    }
}
public class DoLogin{
    public static void main(String[] args) {
        ALogin a =new ALogin();
        a.start();
        BLogin b =new BLogin();
        b.start();
    }
}
當前的用戶名:b 密碼:bb
當前的用戶名:b 密碼:aa

解決這個非線程安全的方法也是使用synchronized,更改代碼如下:

public class LoginServlet{
    private static StringuserNameRef;
    private static StringpasswordRef;
    synchronized public static void login(String userName, String password) {
        userNameRef = userName;
        try {
            if (userName.equals("a")) {
                Thread.sleep(5000);
            }
            passwordRef = password;
            System.out.println("當前的用戶名:" +userNameRef +" 密碼:" +passwordRef);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
當前的用戶名:a 密碼:aa
當前的用戶名:b 密碼:bb

1.3 currentThread()

currentThread方法可返回代碼段正在被哪個線程調(diào)用的信息。

1.4 isAlive()

判斷當前線程是否處于活動狀態(tài)?;顒訝顟B(tài)就是線程已經(jīng)啟動且尚未終止,線程處于正在運行或準備開始運行的狀態(tài),就認為線程是“存活的”。

1.5 sleep()

在指定得毫秒數(shù)內(nèi)讓當前“正在執(zhí)行的線程”休眠(暫停執(zhí)行)。這個“正在執(zhí)行的線程”是指this.currentThread()返回的線程。

1.6 getId()

getId()方法的作用是取得線程的唯一標識。

1.7 停止線程

停止一個線程意味著在線程處理完任務(wù)之前停掉正在做的操作。雖然看起來很簡單,但是必須做好防范措施,以便達到預期效果。
在Java中有三種方法可以終止正在運行的線程:
1)使用退出標志,使線程正常退出,也就是當run方法完成后線程終止。
2)使用stop方法(已經(jīng)被作廢)強行終止線程。但是不推薦使用此方法,因為stopsusped、resume一樣都是作廢過期的方法。
3)使用interrupt方法中斷線程。

1.7.1 停止不了的線程

interrupt方法僅是在當前線程中打了一個停止的標記,并不是真的停止線程。

public class MyThread extends Thread{
    @Override
    public void run() {
        super.run();
        for (int i =0; i <500000; i++) {
            System.out.println("i=" + i);
        }
    }
}
public class Run{
    public static void main(String[] args) {
        MyThread thread =new MyThread();
        thread.start();
        thread.interrupt();
    }
}

從運行結(jié)果來看,調(diào)用interrupt方法并沒有停止線程。

1.7.2 判斷線程是否是停止狀態(tài)

1)this.interrupted():測試當前線程是否已經(jīng)中斷,執(zhí)行后具有將狀態(tài)標志清除為false的功能。
2)this.isInterrupted():測試當前線程是否已經(jīng)中斷,不清除狀態(tài)標志。

1.7.3 能停止的線程-異常法

public class MyThread extends Thread{
    @Override
    public void run() {
        super.run();
        for (int i =0; i <500000; i++) {
            if (this.isInterrupted()) {
                System.out.println("停止狀態(tài) 退出");
                break;
            }
            System.out.println("i=" + i);
        }
    }
}
public class Run{
    public static void main(String[] args) {
        try {
            MyThread thread =new MyThread();
            thread.start();
            Thread.sleep(2000);
            thread.interrupt();
        } catch (Exception e) {
            System.out.println("main catch");
            e.printStackTrace();
        }
        System.out.println("end");
    }
}
i=408980
i=408981
i=408982
i=408983
i=408984
end
停止狀態(tài) 退出

上面示例代碼雖然停止了線程但如果for下面還有語句,還是會運行的,該如何解決語句繼續(xù)運行的問題呢 ?

public class MyThread extends Thread{
    @Override
    public void run() {
        super.run();
        try {
            for (int i =0; i <500000; i++) {
                if (this.isInterrupted()) {
                    System.out.println("停止狀態(tài) 退出");
                    throw new InterruptedException();
                }
                System.out.println("i=" + i);
            }
            System.out.println("for之后運行,線程并未停止");
        } catch (Exception e) {
            System.out.println("MyThread catch");
            e.printStackTrace();
        }
    }
}
public class Run{
    public static void main(String[] args) {
        try {
            MyThread thread =new MyThread();
            thread.start();
            Thread.sleep(2000);
            thread.interrupt();
        } catch (Exception e) {
            System.out.println("main catch");
            e.printStackTrace();
        }
        System.out.println("end");
    }
}
i=410919
i=410920
i=410921
end
停止狀態(tài) 退出
MyThread catch
java.lang.InterruptedException

1.7.4 在沉睡中停止

如果線程在sleep狀態(tài)下停止線程,會是什么效果呢?

public class MyThread extends Thread{
    @Override
    public void run() {
        super.run();
        try {
            System.out.println("run begin");
            Thread.sleep(20000);
            System.out.println("run end");
        } catch (InterruptedException e) {
            System.out.println("在沉睡中被停止!進入catch!" +this.isInterrupted());
            e.printStackTrace();
        }
    }
}
public class Run{
    public static void main(String[] args) {
        try {
            MyThread thread =new MyThread();
            thread.start();
            Thread.sleep(200);
            thread.interrupt();
        } catch (Exception e) {
            System.out.println("main catch");
            e.printStackTrace();
        }
        System.out.println("end");
    }
}
run begin
end
在沉睡中被停止!進入catch!false
java.lang.InterruptedException: sleep interrupted

sleep狀態(tài)下,停止某一線程,會進入catch語句,并清除停止狀態(tài)值,使之變成false

1.7.5 能停止的線程-暴力停止

使用stop方法停止線程則是非常暴力的。

public class MyThread extends Thread{
    private int i =0;
    @Override
    public void run() {
        try {
            while (true) {
                i++;
                System.out.println("i=" +i);
                Thread.sleep(1000);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
public class Run{
    public static void main(String[] args) {
        try {
            MyThread thread =new MyThread();
            thread.start();
            Thread.sleep(8000);
            thread.stop();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
i=1
i=2
i=3
i=4
i=5
i=6
i=7
i=8
i=9

1.7.6 stop()與java.lang.ThreadDeath異常

調(diào)用stop()時會拋出java.lang.ThreadDeath異常。該方法已經(jīng)被作廢,因為如果強制讓線程停止則有可能使一些清理性的工作得不到完成。另外一個情況就是對鎖定的對象進行了解鎖,導致數(shù)據(jù)得不到同步的處理,出現(xiàn)數(shù)據(jù)不一致的問題。

1.7.7 釋放鎖的不良后果

使用stop()釋放鎖將會給數(shù)據(jù)造成不一致性的結(jié)果。如果出現(xiàn)這樣的情況,程序處理的數(shù)據(jù)就有可能遭到破壞,最終導致程序執(zhí)行流程錯誤,一定要特別注意。

public class SynchronizedObject{
    private String userName ="a";
    private String password ="a";
    public String get UserName() {
        return userName;
    }
    public void setUserName(String userName) {
        this.userName = userName;
    }
    public String getPassword() {
        return password;
    }
    public void setPassword(String password) {
        this.password = password;
    }
    public void printString(String userName, String password) {
        try {
            this.userName = userName;
            Thread.sleep(100000);
            this.password = password;
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
public class MyThread extends Thread{
    private SynchronizedObject syncObject;
    public MyThread(SynchronizedObject syncObject) {
        this.syncObject = syncObject;
    }
    @Override
    public void run() {
        syncObject.printString("b", "bbb");
    }
}
public class Run{
    public static void main(String[] args) {
        try {
            SynchronizedObject syncObject =new SynchronizedObject();
            MyThread thread =new MyThread(syncObject);
            thread.start();
            Thread.sleep(8000);
            thread.stop();
            System.out.println(syncObject.getUserName() +"  " +syncObject.getPassword());
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
b a

1.7.8 使用return停止線程

interruptreturn結(jié)合使用也能實現(xiàn)停止線程的效果。

public class MyThread extends Thread{
    @Override
    public void run() {
        while (true) {
            if (this.isInterrupted()) {
                System.out.println("stop");
                return;
            }
            System.out.println("current time=" + System.currentTimeMillis());
        }
    }
}
public class Run{
    public static void main(String[] args) {
        try {
            MyThread thread =new MyThread();
            thread.start();
            Thread.sleep(8000);
            thread.interrupt();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
current time=1587436071260
current time=1587436071260
stop

不過還是建議使用拋異常的方法來實現(xiàn)線程的停止,因為在catch塊中還可以講一場向上拋,是線程停止的時間得以傳播。

1.8 暫停線程

暫停線程意味著此線程還可以恢復運行,在Java多線程中,可以使用suspend()暫停線程,使用resume()恢復線程的執(zhí)行。

1.8.1 suispend與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 class Run{
    public static void main(String[] args) {
        try {
            MyThread thread =new MyThread();
            thread.start();
            Thread.sleep(5000);
            // 暫停線程
            thread.suspend();
            System.out.println("A=" + System.currentTimeMillis() +" i=" + thread.getI());
            Thread.sleep(5000);
            System.out.println("A=" + System.currentTimeMillis() +" i=" + thread.getI());
            // 恢復
            thread.resume();
            Thread.sleep(5000);
            // 繼續(xù)暫停
            thread.suspend();
            System.out.println("B=" + System.currentTimeMillis() +" i=" + thread.getI());
            Thread.sleep(5000);
            System.out.println("B=" + System.currentTimeMillis() +" i=" + thread.getI());
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

A=1587436765560 i=2885201074
A=1587436770560 i=2885201074
B=1587436775560 i=5820785533
B=1587436780560 i=5820785533

1.8.2 suspend與resume方法的缺點-獨占

在使用suspendresume方法時,如果使用不當,極易造成公共的同步對象的獨占,是其他線程無法訪問公共同步對象。

public class SynchronizedObject{
    synchronized public void printString() {
        System.out.println("begin");
        if (Thread.currentThread().getName().equals("a")) {
            System.out.println("a線程一直suspend···");
            Thread.currentThread().suspend();
        }
        System.out.println("end");
    }
}
public class Run{
    public static void main(String[] args) {
        try {
            final SynchronizedObject syncObject =new SynchronizedObject();
            Thread thread =new Thread(new Runnable(){
                @Override
                public void run() {
                    syncObject.printString();
                }
            });
            thread.setName("a");
            thread.start();
            Thread.sleep(1000);
            Thread thread2 =new Thread(new Runnable(){
                @Override
                public void run() {
                    System.out.println("thread2 begin");
                    syncObject.printString();
                }
            });
            thread2.start();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
begin
a線程一直suspend···
thread2 begin

還有另外一種獨占鎖的情況也要格外注意,稍有不慎就會進坑。

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++;
            System.out.println(i);
        }
    }
}
public class Run{
    public static void main(String[] args) {
        try {
            MyThread myThread =new MyThread();
            myThread.start();
            Thread.sleep(1000);
            myThread.suspend();
            System.out.println("main end");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

程序不會再打印“main end”,因為當程序運行到println()方法停止時,同步鎖未釋放。println()源碼如下:

public void println(String x) {
    synchronized (this) {
        print(x);
        newLine();
    }
}

1.8.3 suspend與resume方法的缺點-不同步

在使用suspendresume方法是容易出現(xiàn)因線程暫停而導致數(shù)據(jù)不同步的情況。

public class MyObject{
    private String userName ="1";
    private String password ="11";
    public void setValue(String userName, String password) {
        this.userName = userName;
        if ("a".equals(Thread.currentThread().getName())) {
            System.out.println("線程a暫停");
            Thread.currentThread().suspend();
        }
        this.password = password;
    }

    public void print() {
        System.out.println(userName +"  " +password);
    }
}
public class Run{
    public static void main(String[] args) throws InterruptedException{
        final MyObject myObject =new MyObject();
        Thread thread =new Thread(new Runnable(){
            @Override
            public void run() {
                myObject.setValue("lala", "lalala");
            }
        });
        thread.setName("a");
        thread.start();
        Thread.sleep(1000);
        new Thread(new Runnable(){
            @Override
            public void run() {
                myObject.print();
            }
        }).start();
    }
}
線程a暫停
lala  11

1.9 yield方法

yield方法的作用是放棄當前的CPU資源,將它讓給其他任務(wù)去占用CPU執(zhí)行時間。單放棄的時間不確定,有可能剛剛放棄,馬上又獲得CPU時間片。

public class MyThread extends Thread{
    @Override
    public void run() {
        long beginTime = System.currentTimeMillis();
        int count =0;
        for (int i =0; i <50000000; i++) {
            // Thread.yield();
            count = count + i +1;
        }
        long endTime = System.currentTimeMillis();
        System.out.println("用時:" +(endTime - beginTime) +"毫秒!");
    }
}
public class Run{
    public static void main(String[] args)  {
        MyThread myThread =new MyThread();
        myThread.start();
    }
}
用時:19毫秒!

取消Thread.yield();注釋,再次運行結(jié)果如下所示:

用時:11723毫秒!

1.10 線程的優(yōu)先級

操作系統(tǒng)中,線程和一劃分優(yōu)先級,優(yōu)先級較高的線程得到的CPU資源較多,也就是CPU有限制性優(yōu)先級較高的線程對象中的任務(wù)。設(shè)置線程優(yōu)先級有助于幫線程規(guī)劃器確定下一次選擇哪一個線程來優(yōu)先執(zhí)行。設(shè)置線程優(yōu)先級使用setPriority(),源碼如下:

public final void setPriority(int newPriority) {
    ThreadGroup g;
    checkAccess();
    if (newPriority >MAX_PRIORITY || newPriority
        throw new IllegalArgumentException();
    }
    if((g = getThreadGroup()) !=null) {
        if (newPriority > g.getMaxPriority()) {
            newPriority = g.getMaxPriority();
        }
        setPriority0(priority = newPriority);
    }
}

在Java中,線程的優(yōu)先級分為1至10,這十個等級,如果小于或大于10,則拋出異常 throw new IllegalArgumentException()。

JDK中使用三個常量來預定義優(yōu)先級的值,代碼如下:

/**
* The minimum priority that a thread can have.
*/
public final static int MIN_PRIORITY =1;
/**
* The default priority that is assigned to a thread.
*/
public final static int NORM_PRIORITY =5;
/**
* The maximum priority that a thread can have.
*/
public final static int MAX_PRIORITY =10;

1.10.1 線程優(yōu)先級的繼承特性

在Java中,線程的優(yōu)先級具有繼承性,比如A線程啟動B線程,則B線程的優(yōu)先級與A是一樣的。


public class MyThread2 extends Thread{
    @Override
    public void run() {
        System.out.println("myThread2 run priority="+this.getPriority());
    }
}
public class MyThread1 extends Thread{
    @Override
    public void run() {
        System.out.println("myThread1 run priority="+this.getPriority());
        MyThread2 thread2 =new MyThread2();
        thread2.start();
    }
}
public class Run{
    public static void main(String[] args)  {
        System.out.println("main thread begin priority="+Thread.currentThread().getPriority());
        Thread.currentThread().setPriority(6);
        System.out.println("main thread end priority="+Thread.currentThread().getPriority());
        MyThread1 myThread1 =new MyThread1();
        myThread1.start();
    }
}
main thread begin priority=5
main thread end priority=6
myThread1 run priority=6
myThread2 run priority=6

1.10.2 優(yōu)先級具有規(guī)則性

public class MyThread1 extends Thread{
    @Override
    public void run() {
        long beginTime = System.currentTimeMillis();
        for (int i =0; i <100; i++) {
            Random random =new Random();
            random.nextInt();
        }
        long endTime = System.currentTimeMillis();
        System.out.println("★★★★★ thread1 use time=" +(endTime - beginTime));
    }
}
public class MyThread2 extends Thread{
    @Override
    public void run() {
        long beginTime = System.currentTimeMillis();
        for (int i =0; i <100; i++) {
            Random random =new Random();
            random.nextInt();
        }
        long endTime = System.currentTimeMillis();
        System.out.println("☆☆☆☆☆ thread2 use time=" +(endTime - beginTime));
    }
}
public class Run{
    public static void main(String[] args) {
        for (int i =0; i <5; i++) {
            MyThread1 thread1 =new MyThread1();
            thread1.start();
            MyThread2 thread2 =new MyThread2();
            thread2.setPriority(6);
            thread2.start();
        }
    }
}

第一次執(zhí)行結(jié)果:

☆☆☆☆☆ thread2 use time=0
★★★★★ thread1 use time=0
★★★★★ thread1 use time=0
☆☆☆☆☆ thread2 use time=0
☆☆☆☆☆ thread2 use time=0
★★★★★ thread1 use time=0
☆☆☆☆☆ thread2 use time=0
★★★★★ thread1 use time=4
★★★★★ thread1 use time=3
☆☆☆☆☆ thread2 use time=4

第二次執(zhí)行結(jié)果:

★★★★★ thread1 use time=0
★★★★★ thread1 use time=0
★★★★★ thread1 use time=1
★★★★★ thread1 use time=0
☆☆☆☆☆ thread2 use time=0
★★★★★ thread1 use time=2
☆☆☆☆☆ thread2 use time=1
☆☆☆☆☆ thread2 use time=0
☆☆☆☆☆ thread2 use time=0
☆☆☆☆☆ thread2 use time=0

根據(jù)以上時長可以得出,不要把線程的優(yōu)先級與運行結(jié)果的順序作為衡量的標準,優(yōu)先級較高的線程不一定每次都先執(zhí)行完run()方法中的任務(wù)。他們的關(guān)系具有不確定性隨機性。

1.11 守護線程

Java中有兩種線程,一種是用戶線程,一種是守護線程
當進程中不存在費守護線程了,則守護線程自動銷毀。典型的守護線程就是GC(垃圾回收器)。任何一個守護線程都是整個JVM中所有費守護線程的“保姆”,只要當前JVM實例中存在任何一個非守護線程沒有結(jié)束,守護線程就在工作,當最后一個非守護線程結(jié)束時,守護線程才隨著JVM一同結(jié)束工作。

public class MyThread extends Thread{
    private long i =0;
    @Override
    public void run() {
        try {
            while (true) {
                i++;
                System.out.println("i=" +i);
                Thread.sleep(1000);
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
public class Run{
    public static void main(String[] args) {
        try {
            MyThread myThread =new MyThread();
            myThread.setDaemon(true);
            myThread.start();
            Thread.sleep(5000);
            System.out.println("main thread end, myThread end");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容