1. 程式人生 > 實用技巧 >090_多執行緒(一)

090_多執行緒(一)

目錄
https://www.bilibili.com/video/BV1V4411p7EF/

簡介

  1. 程式:指令和資料的有序集合,其本身沒有任何執行的含義,是一個靜態的概念。
  2. 程序Process:執行程式的一次執行過程,它是一個動態的概念。是系統資源分配的單位。一個程序中可以包含多個執行緒,至少有一個執行緒,否則沒有存在的意義。
  3. 執行緒Thread:CPU排程和執行的單位。
  4. 真正的多執行緒指多核。很多多執行緒是模擬出來的,即在一個CPU的情況下,快速切換執行緒。

執行緒建立

繼承Thread類

  1. 自定義執行緒類繼承Thread類
  2. 重寫run()方法,編寫執行緒執行體
  3. 建立執行緒物件,呼叫start()方法啟動執行緒
package com.qing.demo01;

/**
 * 建立執行緒方式一:繼承Thread類,重寫run()方法,呼叫start()開啟執行緒
 * 注意:執行緒開啟不一定立即執行,由CPU排程執行
 */
public class TestThread1 extends Thread {
    @Override
    public void run() {
        //run方法執行緒體
        for (int i = 0; i < 200; i++) {
            System.out.println("run執行緒--" + i);
        }
    }
    //main執行緒,主執行緒
    public static void main(String[] args) {
        TestThread1 testThread1 = new TestThread1();
        testThread1.start();
        for (int i = 0; i < 200; i++) {
            System.out.println("main執行緒--" + i);
        }
    }
}
main執行緒--83
main執行緒--84
run執行緒--0
run執行緒--1
run執行緒--2
run執行緒--3
run執行緒--4
run執行緒--5
run執行緒--6
run執行緒--7
run執行緒--8
run執行緒--9
run執行緒--10
run執行緒--11
run執行緒--12
run執行緒--13
run執行緒--14
run執行緒--15
run執行緒--16
run執行緒--17
run執行緒--18
run執行緒--19
run執行緒--20
run執行緒--21
run執行緒--22
main執行緒--85
run執行緒--23
run執行緒--24
main執行緒--86
main執行緒--87
main執行緒--88
main執行緒--89
main執行緒--90

網圖下載

package com.qing.demo01;

import org.apache.commons.io.FileUtils;

import java.io.File;
import java.io.IOException;
import java.net.URL;

/**
 * 練習Thread,實現多執行緒同步下載圖片
 */
public class TestThread2 extends Thread {

    private String url;//網路圖片地址
    private String name;//儲存的檔名

    public TestThread2(String url, String name) {
        this.url = url;
        this.name = name;
    }

    @Override
    public void run() {
        WebDownloader webDownloader = new WebDownloader();
        webDownloader.downloader(url, name);
        System.out.println("下載檔案,name: " + name + ",url: " + url);
    }

    public static void main(String[] args) {
        TestThread2 t1 = new TestThread2("https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1605724974060&di=abc373a617b841a8e78b3c53fcbd9b5c&imgtype=0&src=http%3A%2F%2Fimg.ewebweb.com%2Fuploads%2F20191203%2F16%2F1575361350-YwtjLgUXBq.jpg", "1.jpg");
        TestThread2 t2 = new TestThread2("https://ss0.bdstatic.com/70cFuHSh_Q1YnxGkpoWK1HF6hhy/it/u=1578345841,1712921595&fm=26&gp=0.jpg", "2.jpg");
        TestThread2 t3 = new TestThread2("https://ss1.bdstatic.com/70cFuXSh_Q1YnxGkpoWK1HF6hhy/it/u=3131683833,708813674&fm=26&gp=0.jpg", "3.jpg");

        t1.start();
        t2.start();
        t3.start();
    }
}

//下載器
class WebDownloader {
    //下載方法
    public void downloader(String url, String name) {
        try {
            FileUtils.copyURLToFile(new URL(url), new File(name));
        } catch (IOException e) {
            e.printStackTrace();
            System.out.println("IO異常,downloader方法出現問題");
        }
    }
}
下載檔案,name: 2.jpg,url: https://ss0.bdstatic.com/70cFuHSh_Q1YnxGkpoWK1HF6hhy/it/u=1578345841,1712921595&fm=26&gp=0.jpg
下載檔案,name: 3.jpg,url: https://ss1.bdstatic.com/70cFuXSh_Q1YnxGkpoWK1HF6hhy/it/u=3131683833,708813674&fm=26&gp=0.jpg
下載檔案,name: 1.jpg,url: https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1605724974060&di=abc373a617b841a8e78b3c53fcbd9b5c&imgtype=0&src=http%3A%2F%2Fimg.ewebweb.com%2Fuploads%2F20191203%2F16%2F1575361350-YwtjLgUXBq.jpg

實現Runnable介面

  1. 定義類實現Runnable介面
  2. 實現run()方法,編寫執行緒執行體
  3. 傳入類物件建立執行緒物件Thread,呼叫start()方法啟動執行緒
package com.qing.demo01;

//建立執行緒方式二:實現Runnable介面,重寫run()方法,傳入類物件建立Thread物件,呼叫start()方法啟動執行緒
public class TestThread3 implements Runnable {
    @Override
    public void run() {
        for (int i = 0; i < 200; i++) {
            System.out.println("run執行緒--" + i);
        }
    }

    public static void main(String[] args) {
        TestThread3 testThread3 = new TestThread3();
        new Thread(testThread3).start();
        for (int i = 0; i < 200; i++) {
            System.out.println("main執行緒--" + i);
        }
    }
}
main執行緒--0
run執行緒--0
run執行緒--1
run執行緒--2
run執行緒--3
run執行緒--4
run執行緒--5
run執行緒--6
run執行緒--7
run執行緒--8
run執行緒--9
run執行緒--10
run執行緒--11
run執行緒--12
main執行緒--1
main執行緒--2
main執行緒--3
main執行緒--4
main執行緒--5
main執行緒--6
main執行緒--7
main執行緒--8
main執行緒--9
main執行緒--10
main執行緒--11
main執行緒--12
run執行緒--13

繼承Thread類和實現Runnable介面的區別

  1. 繼承Thread類
    1. 子類繼承Thread類具備多執行緒能力
    2. 啟動執行緒:子類物件.start()
    3. 不建議使用:避免單繼承侷限性
  2. 實現Runnable介面
    1. 實現介面Runnable具有多執行緒能力
    2. 啟動執行緒:new Thread(實現類物件).start()
    3. 推薦使用:避免單繼承侷限性,靈活方便,方便同一個物件被多個執行緒使用

併發問題

package com.qing.demo01;

/**
 * 買火車票的例子
 * 多個執行緒同時操作同一個物件
 * 發現問題:多個執行緒操作同一個資源的情況下,執行緒不安全,資料紊亂。
 */
public class TestThread4 implements Runnable {

    //票數
    private int ticketNums = 10;

    @Override
    public void run() {
        while (true) {
            if (ticketNums <= 0) {
                break;
            }
            //模擬延時
            try {
                Thread.sleep(200);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + "-->買到了第" + ticketNums-- + "票");
        }
    }

    public static void main(String[] args) {
        TestThread4 testThread4 = new TestThread4();
        new Thread(testThread4,"楊康").start();
        new Thread(testThread4,"郭靖").start();
        new Thread(testThread4,"黃蓉").start();
    }
}
黃蓉-->買到了第10票
楊康-->買到了第9票
郭靖-->買到了第10票
黃蓉-->買到了第8票
郭靖-->買到了第7票
楊康-->買到了第6票
楊康-->買到了第5票
郭靖-->買到了第5票
黃蓉-->買到了第5票
楊康-->買到了第4票
郭靖-->買到了第2票
黃蓉-->買到了第3票
郭靖-->買到了第1票
黃蓉-->買到了第-1票
楊康-->買到了第0票

龜兔賽跑

package com.qing.demo01;

/**
 * 模擬龜兔賽跑
 */
public class Race implements Runnable {

    //勝利者
    private static String winner;

    @Override
    public void run() {
        for (int i = 1; i <= 100; i++) {
            //模擬兔子睡覺
            if ("兔子".equals(Thread.currentThread().getName()) && i%10==0) {
                try {
                    Thread.sleep(5);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }

            //判斷比賽是否結束
            boolean flag = gameOver(i);
            //如果比賽結束了,終止程式
            if (flag) {
                break;
            }
            System.out.println(Thread.currentThread().getName() + "-->跑了" + i + "步");
        }
    }

    //判斷是否完成比賽
    private boolean gameOver(int steps) {
        //判斷是否有勝利者
        if (winner != null) {
            return true;
        }
        //判斷是否跑完100步
        if (steps >= 100) {
            winner = Thread.currentThread().getName();
            System.out.println("winner is " + winner);
            return true;
        }
        return false;
    }

    public static void main(String[] args) {
        Race race = new Race();
        new Thread(race,"兔子").start();
        new Thread(race,"烏龜").start();
    }
}
烏龜-->跑了98步
烏龜-->跑了99步
winner is 烏龜
兔子-->跑了28步

實現Callable介面

  1. 實現Callable介面,需要返回值型別
  2. 重新call()方法,需要丟擲異常
  3. 建立目標物件
  4. 建立執行服務:ExecutorService service = Executors.newFixedThreadPool(3);
  5. 提交執行:Future r1 = service.submit(t1);
  6. 獲取結果:boolean rs1 = r1.get();
  7. 關閉服務:service.shutdownNow();
package com.qing.demo02;

import com.qing.demo01.TestThread2;
import org.apache.commons.io.FileUtils;

import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.util.concurrent.*;

/**
 * 實現多執行緒同步下載圖片
 * 執行緒建立方式三:實現Callable介面
 * Callable的好處
 * 1.可以定義返回值
 * 2.可以丟擲異常
 */
public class TestCallable implements Callable<Boolean> {

    private String url;//網路圖片地址
    private String name;//儲存的檔名

    public TestCallable(String url, String name) {
        this.url = url;
        this.name = name;
    }

    @Override
    public Boolean call() throws Exception {
        WebDownloader webDownloader = new WebDownloader();
        webDownloader.downloader(url, name);
        System.out.println("下載檔案,name: " + name + ",url: " + url);
        return true;
    }

    public static void main(String[] args) throws ExecutionException, InterruptedException {
        TestCallable t1 = new TestCallable("https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1605724974060&di=abc373a617b841a8e78b3c53fcbd9b5c&imgtype=0&src=http%3A%2F%2Fimg.ewebweb.com%2Fuploads%2F20191203%2F16%2F1575361350-YwtjLgUXBq.jpg", "1.jpg");
        TestCallable t2 = new TestCallable("https://ss0.bdstatic.com/70cFuHSh_Q1YnxGkpoWK1HF6hhy/it/u=1578345841,1712921595&fm=26&gp=0.jpg", "2.jpg");
        TestCallable t3 = new TestCallable("https://ss1.bdstatic.com/70cFuXSh_Q1YnxGkpoWK1HF6hhy/it/u=3131683833,708813674&fm=26&gp=0.jpg", "3.jpg");

        //建立執行服務
        ExecutorService service = Executors.newFixedThreadPool(3);

        //提交執行
        Future<Boolean> r1 = service.submit(t1);
        Future<Boolean> r2 = service.submit(t2);
        Future<Boolean> r3 = service.submit(t3);

        //獲取結果
        boolean rs1 = r1.get();
        boolean rs2 = r2.get();
        boolean rs3 = r3.get();
        System.out.println(rs1);
        System.out.println(rs2);
        System.out.println(rs3);

        //關閉服務
        service.shutdownNow();
    }
}

//下載器
class WebDownloader {
    //下載方法
    public void downloader(String url, String name) {
        try {
            FileUtils.copyURLToFile(new URL(url), new File(name));
        } catch (IOException e) {
            e.printStackTrace();
            System.out.println("IO異常,downloader方法出現問題");
        }
    }
}
下載檔案,name: 3.jpg,url: https://ss1.bdstatic.com/70cFuXSh_Q1YnxGkpoWK1HF6hhy/it/u=3131683833,708813674&fm=26&gp=0.jpg
下載檔案,name: 1.jpg,url: https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1605724974060&di=abc373a617b841a8e78b3c53fcbd9b5c&imgtype=0&src=http%3A%2F%2Fimg.ewebweb.com%2Fuploads%2F20191203%2F16%2F1575361350-YwtjLgUXBq.jpg
下載檔案,name: 2.jpg,url: https://ss0.bdstatic.com/70cFuHSh_Q1YnxGkpoWK1HF6hhy/it/u=1578345841,1712921595&fm=26&gp=0.jpg
true
true
true

靜態代理模式

  1. 真實物件和代理物件都要實現同一個介面
  2. 代理物件要代理真實物件,真實物件作為代理物件的屬性
//真實物件
public class TestThread3 implements Runnable {
    @Override
    public void run() {
        for (int i = 0; i < 200; i++) {
            System.out.println("run執行緒--" + i);
        }
    }

    public static void main(String[] args) {
        TestThread3 testThread3 = new TestThread3();
        new Thread(testThread3).start();
    }
}
//代理物件
public class Thread implements Runnable {
    //真實物件
    private Runnable target;
    
    public Thread(Runnable target) {
        init(null, target, "Thread-" + nextThreadNum(), 0);
    }
    
    @Override
    public void run() {
        if (target != null) {
            target.run();
        }
    }
}

靜態代理模式的好處

  1. 代理物件可以做很多真實物件做不了的事情
  2. 真實物件專注做自己的事情

Lambda表示式

為什麼要使用Lambda表示式

  1. 避免匿名內部類定義過多
  2. 可以讓程式碼看起來簡潔
  3. 去掉了一堆沒有意義的程式碼,只留下核心的邏輯

函式式介面Functional Interface

  1. 定義:任何介面,如果只包含唯一一個抽象方法,那麼它就是一個函式式介面。
public interface Runnable {
    public abstract void run();
}
  1. 對於函式式介面,可以通過lambda表示式來建立該介面的物件。
  2. 推導lambda表示式
package com.qing.lambda;

/**
 * 推導lambda表示式
 */
public class TestLambda01 {
    //3.靜態內部類
    static class Like2 implements ILike {
        @Override
        public void lambda() {
            System.out.println("I like lambda2");
        }
    }

    public static void main(String[] args) {
        ILike like = new Like();
        like.lambda();

        like = new Like2();
        like.lambda();

        //4.區域性內部類
        class Like3 implements ILike {
            @Override
            public void lambda() {
                System.out.println("I like lambda3");
            }
        }

        like = new Like3();
        like.lambda();

        //5.匿名內部類:沒有類的名稱,必須藉助介面或者父類
        like = new ILike() {
            @Override
            public void lambda() {
                System.out.println("I like lambda4");
            }
        };
        like.lambda();

        //6.用lambda表示式簡化
        like = ()->{
            System.out.println("I like lambda5");
        };
        like.lambda();
    }
}

//1.定義一個函式式介面:只有一個抽象方法的介面
interface ILike {
    void lambda();
}

//2.實現類
class Like implements ILike {
    @Override
    public void lambda() {
        System.out.println("I like lambda");
    }
}
I like lambda
I like lambda2
I like lambda3
I like lambda4
I like lambda5
package com.qing.lambda;

/**
 * lambda表示式
 */
public class TestLambda01 {
    public static void main(String[] args) {
        ILike like = ()->{
            System.out.println("I like lambda5");
        };
        like.lambda();
    }
}

//1.定義一個函式式介面:只有一個抽象方法的介面
interface ILike {
    void lambda();
}
  1. 有引數lambda表示式
package com.qing.lambda;

public class TestLambda02 {
    public static void main(String[] args) {
        //1.lambda表示式簡化
        ILove love = (int i)->{
            System.out.println("I love you-->" + i);
        };
        love.love(520);

        //簡化1.簡化引數型別
        love = (i)->{
            System.out.println("I love you-->" + i);
        };
        love.love(521);

        //簡化2.簡化引數括號
        love = i->{
            System.out.println("I love you-->" + i);
        };
        love.love(522);

        //簡化3.簡化方法括號
        love = i->System.out.println("I love you-->" + i);
        love.love(523);
    }
}

interface ILove {
    void love(int i);
}
I love you-->520
I love you-->521
I love you-->522
I love you-->523
package com.qing.lambda;

public class TestLambda02 {
    public static void main(String[] args) {
        ILove love = i->System.out.println("I love you-->" + i);
        love.love(523);
    }
}

interface ILove {
    void love(int i);
}

執行緒狀態

  1. 建立狀態
  2. 就緒狀態
  3. 執行狀態
  4. 阻塞狀態
  5. 死亡狀態



執行緒停止

  1. 不推薦使用JDK提供的stop()、destroy()方法(已廢棄)。
  2. 推薦執行緒自己停止下來。
  3. 建議使用一個標誌位進行終止變數,當flag=false,則終止執行緒執行。
package com.qing.state;

/**
 * 測試停止執行緒
 * 1.建議執行緒正常停止-->利用次數,不建議死迴圈。
 * 2.建議使用標誌位-->設定一個標誌位。
 * 3.不要使用stop()或destroy()等過時或者JDK不建議使用的方法。
 */
public class TestStop implements Runnable {

    //1.設定一個標誌位
    private boolean flag = true;

    @Override
    public void run() {
        int i = 0;
        while (flag) {
            System.out.println("run...Thread" + i++);
        }
    }

    //2.設定一個公開的方法停止執行緒:轉換標誌位
    public void stop() {
        this.flag = false;
    }

    public static void main(String[] args) {
        TestStop testStop = new TestStop();
        new Thread(testStop).start();

        for (int i = 0; i < 1000; i++) {
            System.out.println("main..." + i);
            if (i == 900) {
                //呼叫stop方法切換標誌位,讓執行緒停止
                testStop.stop();
                System.out.println("執行緒該停止了");
            }
        }
    }
}
main...899
main...900
run...Thread1366
run...Thread1367
執行緒該停止了
main...901
main...902

執行緒休眠

  1. sleep(毫秒)指定當前執行緒阻塞的毫秒數。
  2. sleep存在異常InterruptedException。
  3. sleep時間達到後執行緒進入就緒狀態。
  4. sleep可以模擬網路延時、倒計時等。
  5. 每一個物件都有一個鎖,sleep不會釋放鎖。

模擬網路延時:放大問題發生可能性

package com.qing.state;

/**
 * 模擬網路延時:放大問題發生可能性。
 */
public class TestSleep implements Runnable {

    //票數
    private int ticketNums = 10;

    @Override
    public void run() {
        while (true) {
            if (ticketNums <= 0) {
                break;
            }
            //模擬延時
            try {
                Thread.sleep(200);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + "-->買到了第" + ticketNums-- + "票");
        }
    }

    public static void main(String[] args) {
        TestSleep testSleep = new TestSleep();
        new Thread(testSleep,"楊康").start();
        new Thread(testSleep,"郭靖").start();
        new Thread(testSleep,"黃蓉").start();
    }
}
郭靖-->買到了第9票
楊康-->買到了第8票
黃蓉-->買到了第10票
郭靖-->買到了第7票
楊康-->買到了第6票
黃蓉-->買到了第5票
黃蓉-->買到了第4票
楊康-->買到了第3票
郭靖-->買到了第2票
楊康-->買到了第1票
郭靖-->買到了第-1票
黃蓉-->買到了第0票

模擬倒計時

package com.qing.state;

/**
 * 模擬倒計時
 */
public class TestSleep2 {

    public static void tenDown() throws InterruptedException {
        int num = 10;
        while (true) {
            Thread.sleep(1000);
            System.out.println(num--);
            if (num <= 0) {
                break;
            }
        }
    }

    public static void main(String[] args) {
        try {
            tenDown();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
10
9
8
7
6
5
4
3
2
1

模擬時鐘

package com.qing.state;

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

/**
 * 模擬時鐘
 */
public class TestSleep3 {
    public static void main(String[] args) {
        SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss");
        while (true) {
            try {
                System.out.println(sdf.format(new Date()));
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}
23:45:39
23:45:40
23:45:41
23:45:42
23:45:43

執行緒禮讓

  1. 禮讓執行緒,讓當前正在執行的執行緒暫停,但不阻塞。
  2. 將執行緒從執行狀態轉為就緒狀態。
  3. 讓CPU重新排程,禮讓不一定成功,由CPU決定。
package com.qing.state;

/**
 * 測試禮讓執行緒
 * 禮讓不一定成功,由CPU決定
 */
public class TestYield {
    public static void main(String[] args) {
        new Thread(() -> {
            System.out.println(Thread.currentThread().getName() + "執行緒開始執行");
            Thread.yield();
            System.out.println(Thread.currentThread().getName() + "執行緒執行完成");
        },"a").start();
        new Thread(() -> {
            System.out.println(Thread.currentThread().getName() + "執行緒開始執行");
            System.out.println(Thread.currentThread().getName() + "執行緒執行完成");
        },"b").start();
    }
}
//禮讓成功
a執行緒開始執行
b執行緒開始執行
b執行緒執行完成
a執行緒執行完成
//禮讓不成功
a執行緒開始執行
a執行緒執行完成
b執行緒開始執行
b執行緒執行完成

執行緒強制執行

  1. join合併執行緒,待此執行緒執行完成後,再執行其他執行緒,其他執行緒阻塞。
  2. 可以想象成插隊。
package com.qing.state;

/**
 * 測試join方法,想象為插隊
 */
public class TestJoin {
    public static void main(String[] args) throws InterruptedException {
        Thread thread = new Thread(() -> {
            for (int i = 0; i < 500; i++) {
                System.out.println("執行緒vip來了" + i);
            }
        });
        thread.start();

        for (int i = 0; i < 500; i++) {
            if (i == 100) {
                thread.join();
            }
            System.out.println("main執行緒" + i);
        }
    }
}
main執行緒98
main執行緒99
執行緒vip來了82
執行緒vip來了83
執行緒vip來了84
...
執行緒vip來了498
執行緒vip來了499
main執行緒100
main執行緒101

執行緒狀態觀測

public class Thread implements Runnable {
    public enum State {
        NEW,
        RUNNABLE,
        BLOCKED,
        WAITING,
        TIMED_WAITING,
        TERMINATED;
    }
}

執行緒狀態

  1. NEW:未啟動狀態。
  2. RUNNABLE:就緒狀態。
  3. BLOCKED:阻塞狀態。
  4. WAITING:阻塞狀態。
  5. TIMED_WAITING:阻塞狀態。
  6. TERMINATED:死亡狀態。
package com.qing.state;

/**
 * 觀察測試執行緒的狀態
 */
public class TestState {
    public static void main(String[] args) throws InterruptedException {
        Thread thread = new Thread(()->{
            for (int i = 0; i < 5; i++) {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("--------------------------");
            }
        });

        //觀察狀態
        Thread.State state = thread.getState();
        System.out.println(state);//NEW

        //觀察啟動後狀態
        thread.start();
        state = thread.getState();
        System.out.println(state);//RUNNABLE

        //只要執行緒不終止,就一直輸出狀態
        while (state != Thread.State.TERMINATED) {
            Thread.sleep(500);
            state = thread.getState();
            System.out.println(state);
        }
    }
}
NEW
RUNNABLE
TIMED_WAITING
TIMED_WAITING
--------------------------
TIMED_WAITING
--------------------------
TIMED_WAITING
TIMED_WAITING
--------------------------
TIMED_WAITING
TIMED_WAITING
--------------------------
TIMED_WAITING
TIMED_WAITING
--------------------------
TERMINATED

執行緒優先順序

  1. Java提供一個執行緒排程器來監控程式中啟動後進入就緒狀態的所有執行緒,執行緒排程器按照優先順序決定應該排程哪個執行緒來執行。
  2. 執行緒的優先順序用數字表示,範圍從1~10。
    1. Thread.MIN_PRIORITY = 1;
    2. Thread.MAX_PRIORITY = 10;
    3. Thread.NORM_PRIORITY = 5;
  3. 使用以下方式改變或獲取優先順序。
    1. getPriority()
    2. setPriority(int xxx)
  4. 優先順序低只是意味著獲得排程的概率低,並不是優先順序低就不會被呼叫了,這都是看CPU的排程。
  5. 優先順序高的大部分情況先執行,但並不絕對。
package com.qing.state;

/**
 * 測試執行緒的優先順序
 */
public class TestPriority {
    public static void main(String[] args) {
        //主執行緒預設優先順序
        System.out.println(Thread.currentThread().getName() + "-->" + Thread.currentThread().getPriority());

        MyPriority myPriority = new MyPriority();

        Thread t1 = new Thread(myPriority);
        Thread t2 = new Thread(myPriority);
        Thread t3 = new Thread(myPriority);
        Thread t4 = new Thread(myPriority);
        Thread t5 = new Thread(myPriority);
        Thread t6 = new Thread(myPriority);

        //先設定優先順序在啟動執行緒
        t1.start();
        t2.setPriority(Thread.MIN_PRIORITY);
        t2.start();
        t3.setPriority(Thread.NORM_PRIORITY);
        t3.start();
        t4.setPriority(Thread.MAX_PRIORITY);
        t4.start();
        t5.setPriority(3);
        t5.start();
        t6.setPriority(7);
        t6.start();
    }
}

class MyPriority implements Runnable {

    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName() + "-->" + Thread.currentThread().getPriority());
    }
}
main-->5
Thread-3-->10
Thread-5-->7
Thread-1-->1
Thread-0-->5
Thread-4-->3
Thread-2-->5

守護執行緒(daemon)

  1. 執行緒分為使用者執行緒和守護執行緒。
  2. 虛擬機器必須確保使用者執行緒執行完畢。
  3. 虛擬機器不用等待守護執行緒執行完畢。
  4. 守護執行緒,如:後臺記錄操作日誌,監控記憶體,垃圾回收等待等。
package com.qing.state;

/**
 * 測試守護執行緒
 */
public class TestDaemon {
    public static void main(String[] args) {
        Thread god = new Thread(()->{
            while (true) {
                System.out.println("上帝保佑你");
            }
        });
        //設定為守護執行緒,預設是false,表示是使用者執行緒,正常的執行緒都是使用者執行緒
        god.setDaemon(true);
        god.start();
        Thread you = new Thread(()->{
            for (int i = 0; i < 100; i++) {
                System.out.println(i);
            }
            System.out.println("===========================");
        });
        you.start();
    }
}
上帝保佑你
上帝保佑你
上帝保佑你
0
1
2
3
97
98
99
上帝保佑你
上帝保佑你
上帝保佑你
===========================
上帝保佑你
上帝保佑你
上帝保佑你