首頁  >  文章  >  Java  >  Java執行緒中的start方法和run方法怎麼使用

Java執行緒中的start方法和run方法怎麼使用

WBOY
WBOY轉載
2023-04-20 08:58:061587瀏覽

    start方法和run方法

    $start()$方法用來啟動一個線程,這時此執行緒處於就緒 (可運行)狀態,並沒有運行,一旦得到$cpu$時間片,就開始執行$run()$方法。而直接呼叫$run()$方法,只是呼叫了一個類別裡的方法,其本質上還是在當前執行緒中執行的,因此只有使用$start()$方法來呼叫$run()$方法才能實現真正的多線程。

    範例程式碼

    @Slf4j(topic = "c.Test4")
    public class Test4 {
        public static void main(String[] args) {
            Thread t1 = new Thread("t1"){
                @Override
                public void run() {
                    log.debug("running");
                }
            };
            t1.run();
        }
    }

    上述程式碼是直接呼叫的$run()$方法。可以看到列印訊息裡,是$main$執行緒執行了這個方法。

    @Slf4j(topic = "c.Test4")
    public class Test4 {
        public static void main(String[] args) {
            Thread t1 = new Thread("t1"){
                @Override
                public void run() {
                    log.debug("running");
                }
            };
            t1.start();
        }
    }

    而如果使用$start()$方法啟動,才是真正的由$t1$執行緒執行的$run$方法。

    注意

    要注意的是,當$Thread$物件呼叫了$start()$方法後,就會進入就緒狀態,處於就緒狀態時無法再呼叫$start() $方法,否則就會拋出$IllegalThreadStateException$異常,如下程式碼所示

    @Slf4j(topic = "c.Test4")
    public class Test4 {
        public static void main(String[] args) {
            Thread t1 = new Thread("t1"){
                @Override
                public void run() {
                    log.debug("running");
                }
            };
            t1.start();
            t1.start();
        }
    }

    異常訊息:

    Java執行緒中的start方法和run方法怎麼使用

    ##sleep方法與yield方法

    sleep

    • 呼叫$sleep()$方法會讓目前執行緒從$Running$狀態變成$Time Waiting$狀態(阻塞)

    • 其它線程可以使用$interrupt$方法打斷正在睡眠的線程,此時$sleep$方法會拋出InterruptedException

    • ##睡眠結束後的執行緒未必會立刻得到執行
    • 建議用$TimeUnit$的$sleep$取代$Thread$的$sleep$來獲得更好的可讀性範例程式碼
    • @Slf4j(topic = "c.Test5")
      public class Test5 {
          public static void main(String[] args) {
              Thread t1 = new Thread("t1"){
                  @Override
                  public void run() {
                      try {
                          Thread.sleep(2000);
                      } catch (InterruptedException e) {
                          e.printStackTrace();
                      }
                  }
              };
              t1.start();
              log.debug("t1 state {}", t1.getState());
              //让主线程休眠500ms
              try {
                  Thread.sleep(500);
              } catch (InterruptedException e) {
                  e.printStackTrace();
              }
              log.debug("t1 state {}", t1.getState());
          }
      }
      //17:13:21.729 [main] DEBUG c.Test5 - t1 state RUNNABLE
      //17:13:22.245 [main] DEBUG c.Test5 - t1 state TIMED_WAITING
    • 上述程式碼中,先啟動$t1$線程,此時列印執行緒的狀態應該是處於$RUNNABLE$狀態,而讓主執行緒休眠是防止主執行緒先執行列印,但還未進入到$sleep()$狀態。當執行到$run()$裡邊的$sleep$方法時,執行緒進入$TIMED WAITING$狀態
    @Slf4j(topic = "c.Test6")
    public class Thread6 {
        public static void main(String[] args) throws InterruptedException {
            Thread t1 = new Thread("t1") {
                @Override
                public void run() {
                    try {
                        log.debug("enter sleep");
                        Thread.sleep(2000);
                    } catch (InterruptedException e) {
                        log.debug("wake up");
                        e.printStackTrace();
                    }
                }
            };
            t1.start();
            Thread.sleep(1000);
            log.debug("interrupt t1");
            //被唤醒
            t1.interrupt();
        }
    }

    #執行結果

    Java執行緒中的start方法和run方法怎麼使用上述程式碼中,當$start$方法啟動後,$t1$線程進入睡眠狀態,列印提示訊息,睡眠時間為$2s$,在$main$線程中睡眠$1s$後打斷$t1$執行緒的睡眠,提示打斷訊息,並且呼叫$interrupt()$方法,此時執行緒被打斷,拋出異常。

    Java執行緒中的start方法和run方法怎麼使用$TimeUnit$類別中新增了以什麼單位去睡眠,可讀性更好,但是本質上沒區別,只是進行了單位換算

    TimeUnit.SECONDS.sleep(1);//该语句作用是睡眠一秒

    yield

    呼叫$yield$會讓當前行程從$Running$進入到$Runnable$就緒狀態,然後調度執行其他執行緒具體的實作依賴作業系統的任務調度器,(即當任務調度器中沒有其他任務時,即使讓出$cpu$,也會繼續執行該線程)$sleep$執行後是進入阻塞狀態,此時睡眠時間不結束,就不會分配$cpu$給該線程,但是$yield$是進入就緒狀態,即如果沒有其他線程需要執行,那麼也會給該線程分配時間片,這是$sleep$和$yield$的最大區別線程優先權

    線程優先級

    會提示調度器優先調度該線程,但它只是一個提示,調度器可以忽略他

    如果$cpu$比較忙,那麼優先順序高的會獲得更多的時間片,可$cpu$空閒時,優先權幾乎沒有


    sleep的應用-防止cpu佔用100%

    #在沒有利用$cpu$來計算時,不要讓$while(true )$空轉浪費$cpu$,這時可以使用$yield$或$sleep$來讓$cpu$的使用權交給其他程式

    while (true) {
        try {
            Thread.sleep(50);
        } catch (InterruptedException e) {
            e.printStackTrace();
      }
    }

    可以使用$wait$或條件變數達到類似的效果

    不同的是後兩者都需要加鎖,並且需要對應的喚醒操作,一般適用於要進行同步的場景

    $sleep$適用於無需鎖定同步的場景

    join方法

    以下程式的列印結果:

    @Slf4j(topic = "c.Test6")
    public class Test6 {
        static int r = 0;
        public static void main(String[] args) {
            test();
        }
        private static void test() {
            log.debug("开始");
            Thread t = new Thread("t1") {
                @Override
                public void run() {
                    log.debug("开始");
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    log.debug("结束");
                    r = 10;
                }
            };
            t.start();
            log.debug("r的值是{}", r);
            log.debug("结束");
        }
    }

    Java執行緒中的start方法和run方法怎麼使用#因為主執行緒和$t1$執行緒是並行的,$t1$執行緒需要$1s$後才能計算出$r$的值,而主執行緒一開始就要列印$r$的值,因此列印的值為0

    ##解決方法:

    在$t.start();$後邊加上$t.join();$即可。 $join$的作用是等待某個執行緒運行結束。 以呼叫方的角度來說,需要等待結果回傳才能繼續執行就是同步,不需要等待回傳結果就能繼續執行的就是非同步。


    因此$join$方法實際上是讓其同步執行Java執行緒中的start方法和run方法怎麼使用

    #有實效的等待

    $join(毫秒)$方法裡可以有一個參數是傳入等待的時間,如果執行緒執行時間大於等待時間,等待時間到了之後,就會停止等待。如果執行緒執行時間小於等待時間,則執行緒執行完畢之後,等待也會跟著結束。不會把設定的等待時間過完。

    interrupt方法

    打断$sleep, wait, join$的线程,即打断阻塞状态的线程
    打断$sleep$的线程,会清空打断状态

    @Slf4j(topic = "c.Test7")
    public class Test7 {
        public static void main(String[] args) throws InterruptedException {
            Thread t = new Thread("t1"){
                @Override
                public void run() {
                    log.debug("sleep...");
                    try {
                        Thread.sleep(5000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            };
            t.start();
            Thread.sleep(1000);
            log.debug("interrupt");
            t.interrupt();
            log.debug("打断标记: {}", t.isInterrupted());
        }
    }

    Java執行緒中的start方法和run方法怎麼使用

    打断正常运行的线程,不会清空打断状态

    因此我们可以在线程中判断打断标记,来决定是否被打断,以及执行被打断之前的收尾工作。

    @Slf4j(topic = "c.Test8")
    public class Test8 {
        public static void main(String[] args) throws InterruptedException {
            Thread t = new Thread("t1"){
                @Override
                public void run() {
                    while (true) {
                        if (Thread.currentThread().isInterrupted()) {
                            log.debug("线程被打断了");
                            break;
                        }
                    }
                }
            };
            t.start();
            Thread.sleep(1000);
            log.debug("interrupt");
            t.interrupt();
        }
    }

    守护线程

    默认情况下,$java$需要等待所有线程都运行结束,才会结束。有一种特殊的线程叫做守护线程,只要其他非守护线程运行结束了,即使守护线程的代码没有执行完毕,也会强制结束。

    @Slf4j(topic = "c.Test10")
    public class Test10 {
        public static void main(String[] args) throws InterruptedException {
            Thread t = new Thread("t1") {
                @Override
                public void run() {
                    while (true) {
    
                    }
                }
            };
            //设置线程为守护线程
            t.setDaemon(true);
            t.start();
            Thread.sleep(1000);
            log.debug("主线程结束");
        }
    }

    如果不把$t$设置为守护线程,则因为线程内部的死循环,导致程序不会结束运行。

    以上是Java執行緒中的start方法和run方法怎麼使用的詳細內容。更多資訊請關注PHP中文網其他相關文章!

    陳述:
    本文轉載於:yisu.com。如有侵權,請聯絡admin@php.cn刪除