首頁 > 軟體

Java十分鐘入門多執行緒中篇

2022-03-10 13:00:39

舉例說明:

我們知道飛機在天上飛行是有固定的航線(可以理解成執行緒),每個機場都有最大的執行負載能力,當執行情況超過了負載能力的時候,這就需要塔臺排程參與,會根據每架飛機的優先順序排序。當在航線的時候,如果出現緊急情況,會讓其他飛機避讓,讓這架飛機優先順序提高,先降落。這就是排程,計算機程式執行緒執行也是這樣的。

1、執行緒的排程:

在Java多執行緒中,主要可以通過下面四個方法來分配CPU的使用權:

  • 設定優先順序(Priority) 設定執行緒的優先順序,值是1-10
  • 休眠(sleep) 單位毫秒,讓本執行緒屬於阻塞狀態,CPU會執行其他執行緒
  • 強制執行(join) 讓這個執行緒強制獲取CPU資源來執行
  • 禮讓(yield) 暫停正在執行的執行緒,讓其他執行緒先執行,執行完了在接著執行

1、設定優先順序(Priority):

有兩個執行緒,分別設定最大優先順序和最小優先順序:

public class MyThread implements Runnable{

    @Override
    public void run() {
        for (int i = 0; i < 5; i++) {
            System.out.println(Thread.currentThread().getName()+"正在執行:"+i);
        }
    }

    public static void main(String[] args) {
        Thread t1 = new Thread(new MyThread(),"執行緒A:");
        Thread t2 = new Thread(new MyThread(),"執行緒B***:");

        //設定優先順序: 最高為10  最低為1
        t1.setPriority(Thread.MAX_PRIORITY);
        t2.setPriority(Thread.MIN_PRIORITY);

        //顯示執行緒優先順序:
        System.out.println("執行緒A的優先順序是:"+t1.getPriority());
        System.out.println("執行緒B的優先順序是:"+t2.getPriority());

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

結果:

2、休眠(sleep)

Thread.sleep();--------單位是毫秒,讓本執行緒屬於阻塞狀態,CPU會執行其他執行緒:

public class ThreadSleep {
    public static void main(String[] args) {
        sleepTime(5);
    }

    private static void sleepTime(int time) {
        for (int i = 0; i < 5; i++) {
            System.out.println("主執行緒執行了:"+i+"s");
            try{
                //讓執行緒休眠,進入阻塞狀態
                Thread.sleep(1000); //休眠時間為1000毫秒
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

結果:

3、強制執行(join)

顧名思義,就是讓某個執行緒強制進入執行:

子執行緒:

public class MyThread {
}

測試類:

public class Test {
    public static void main(String[] args) throws InterruptedException {

        Thread t1=  new Thread(new MyThread(),"我是子執行緒");
        t1.start();
        //當主執行緒執行任務1-10時,如果執行到5就讓子執行緒t1強制進來執行,直到執行完了才讓主執行緒繼續執行任務
        for (int i = 0; i < 6; i++) {
            if (i==2){
                t1.join();
            }
            System.out.println(Thread.currentThread().getName()+"正在執行:"+i);
        }
    }
}

結果:

4、禮讓(yield)

暫停正在執行的執行緒,讓其他執行緒先執行,執行完了在接著執行:

public class MyThread implements Runnable{

    //執行緒禮讓,讓本線執行緒阻塞,其他執行緒先執行
    //這裡就是A執行緒執行二次後,禮讓,讓B 執行緒先執行
    //也是理論上的,就是不管怎麼樣第二次後面肯定讓B先執行,但是後面就隨機了
    @Override
    public void run() {
        for (int i = 0; i < 5; i++) {
            System.out.println(Thread.currentThread().getName()+"正在執行:"+i);
            if(i == 2){
                Thread.yield();
            }
        }
    }
}

測試類:

public class Test {
    public static void main(String[] args) {

        Thread t1 = new Thread(new MyThread(),"執行緒A:");
        Thread t2 = new Thread(new MyThread(),"----執行緒B");

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

結果:

2、定時器執行緒:

定時器就是可以設定某個時間點來執行某個事件,比如系統每週刪除依次紀錄檔檔案,或者在指定的日期關閉系統,定時器本身就是一個執行緒任務來在指定的時候執行該任務。定時器是繼承TimerTask來重寫run方法,專門處理定時任務。

演示Demo:

public class MyThread extends TimerTask {

    @Override
    public void run() {
        //把任務定義在run方法中
        showMyself();
    }

    public void showMyself(){
        System.out.println("被Run方法執行的");
    }
}

測試類:

public class Test {
    public static void main(String[] args) {

        Timer timer = new Timer();

        //設定5秒後執行這個任務,並且每1秒重複執行這個任務

        timer.schedule(new MyThread(),5000,1000);

    }
}

結果:

3、執行緒的同步:

首先我們先看一個demo:

建立了兩個執行緒物件,一個執行緒A任務用於執行print1,另一個執行緒B任務用於執行print2:

    public void print1(){
        System.out.print("中");
        System.out.println("國");
    }

    public void print2(){
        System.out.print("浙");
        System.out.println("江");
    }
}

測試類:

public class Test {
    public static void main(String[] args) {

        Printer p = new Printer();
        //A:
        new Thread(new Runnable() {
            @Override
            public void run() {
                while(true){
                    p.print1();
                }
            }
        },"執行緒A:").start();

        //B:
        new Thread("執行緒B:"){
            @Override
            public void run(){
                while (true){
                    p.print2();
                }
            }
        }.start();
    }
}

這個程式就是當執行緒A執行的時候,輸出中國,當B執行的時候,輸出浙江,理論上是沒有任何問題,但是我們看一下結果:

我們發現出問題了,其實這就是非執行緒同步(非同步):

  • 同步:提交請求->等待伺服器處理->處理完返回 這個期間使用者端瀏覽器不能幹任何事
  • 非同步:請求通過事件觸發->伺服器處理(這是瀏覽器仍然可以作其他事情)->處理完畢

其實非執行緒同步在有些系統是很危險的問題,比如12306,如果使用非執行緒同步,那麼後果可想而知,那麼該如何同步呢? 這裡介紹一個常用的方法,就是上鎖:

如果兩端程式碼(兩個執行緒任務)是同步的,那麼CPU同一時間只能執行一個任務,相當於給該執行緒上了一把鎖, 在該執行緒沒有執行完這段程式碼或者任務的時候,其他執行緒是不能佔用CPU資源執行任務的。直到執行完了該線 程的程式碼,其他執行緒才可以執行。

更好的理解(舉例):

你去公共廁所上廁所(大的),當你進去後,需要把門關上並鎖著,這就是上鎖,為了保證你正常的結束(保證執行緒正常執行完),這期間其他人是不能進來的。

Synchronized 鎖:

兩個執行緒任務使用同一個物件為鎖,那麼兩個執行緒方法是同步的 我們把上面的方法上個鎖:

class Demo {
}

public class Printer {

   //建立任意一個物件,只要是物件相同就是鎖相同,就是同步的!
    Demo d = new Demo();  

    public void print1(){
    	//當進入print1這個方法時,synchronized就給這個方法上了一個鎖
        synchronized (d){     
            System.out.print("中");
            System.out.println("國");
        }
    }

    public void print2(){
        //當進入print2這個方法時,synchronized也給這個方法上了一個鎖
        synchronized (d){
            System.out.print("浙");
            System.out.println("江");
        }
    }

}

這樣輸出後就不會出現上面的那個問題,這裡就不髮結果截圖啦。大家可以自己試一試,看看是否解決了這個問題~

我們還可以把鎖直接定義在方法上,比如這樣子:

    public static synchronized void print1(){
            System.out.print("中");
            System.out.println("國");
    }

如果是靜態方法,上鎖的方式是通過.class位元組碼物件:

    public static void print1() {
        synchronized (Printer.class) {
            System.out.print("中");
            System.out.println("國");
        }
    }

小結: 這篇文章主要介紹了執行緒的排程(四種)、執行緒的定時以及執行緒的同步,內容也不少,多執行緒是Java的一個難點,也沒用一些華麗的圖片來演示,在學習過程中會比較枯燥,包括我自己也是。大家也可以看看視訊,加深理解和印象!

到此這篇關於Java十分鐘入門多執行緒中篇的文章就介紹到這了,更多相關Java 多執行緒內容請搜尋it145.com以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援it145.com!


IT145.com E-mail:sddin#qq.com