<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
$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(); } }
異常資訊:
@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(); } }
執行結果
上述程式碼中,當$start$方法啟動後,$t1$執行緒進入睡眠狀態,列印提示資訊,睡眠時間為$2s$,在$main$執行緒中睡眠$1s$後打斷$t1$執行緒的睡眠,提示打斷資訊,並且呼叫$interrupt()$方法,此時執行緒被打斷,丟擲異常。
$TimeUnit$類中新增了以什麼單位去睡眠,可讀性更好,但是本質上沒區別,只是進行了單位換算
TimeUnit.SECONDS.sleep(1);//該語句作用是睡眠一秒
呼叫$yield$會讓當前程序從$Running$進入到$Runnable$就緒狀態,然後排程執行其他執行緒具體的實現依賴於作業系統的任務排程器,(即當任務排程器中沒有其他任務時,即使讓出$cpu$,也會繼續執行該執行緒)$sleep$執行後是進入阻塞狀態,此時睡眠時間不結束,就不會分配$cpu$給該執行緒,但是$yield$是進入就緒狀態,即如果沒有其他執行緒需要執行,那麼還會給該執行緒分配時間片,這是$sleep$和$yield$的最大區別執行緒優先順序
會提示排程器優先排程該執行緒,但它僅僅是一個提示,排程器可以忽略他
如果$cpu$比較忙,那麼優先順序高的會獲得更多的時間片,可$cpu$空閒時,優先順序幾乎沒有
在沒有利用$cpu$來計算時,不要讓$while(true)$空轉浪費$cpu$,這時可以可以使用$yield$或者$sleep$來讓$cpu$的使用權交給其他程式
while (true) { try { Thread.sleep(50); } catch (InterruptedException e) { e.printStackTrace(); } }
可以使用$wait$或者條件變數達到類似的效果
不同的是後兩者都需要加鎖,並且需要相應的喚醒操作,一般適用於要進行同步的場景
$sleep$適用於無需鎖同步的場景
以下程式的列印結果:
@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("結束"); } }
因為主執行緒和$t1$執行緒是並行的,$t1$執行緒需要$1s$後才能計算出$r$的值,而主執行緒一開始就要列印出$r$的值,因此列印的值為0
解決方法:
在$t.start();$後邊加上$t.join();$即可。$join$的作用是等待某執行緒執行結束。
以呼叫方的角度來說,需要等待結果返回才能繼續執行就是同步,不需要等待返回結果就能繼續執行的就是非同步。
因此$join$方法實際上是讓其同步執行
$join(毫秒)$方法裡可以有一個引數是傳入等待的時間,如果執行緒執行時間大於等待時間,則等待時間到了之後,就會停止等待。如果執行緒執行時間小於等待時間,則執行緒執行完畢之後,等待也會跟著結束。不會把設定的等待時間過完。
打斷$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()); } }
因此我們可以線上程中判斷打斷標記,來決定是否被打斷,以及執行被打斷之前的收尾工作。
@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方法)的文章就介紹到這了,更多相關Java執行緒方法內容請搜尋it145.com以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援it145.com!
相關文章
<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
综合看Anker超能充系列的性价比很高,并且与不仅和iPhone12/苹果<em>Mac</em>Book很配,而且适合多设备充电需求的日常使用或差旅场景,不管是安卓还是Switch同样也能用得上它,希望这次分享能给准备购入充电器的小伙伴们有所
2021-06-01 09:31:42
除了L4WUDU与吴亦凡已经多次共事,成为了明面上的厂牌成员,吴亦凡还曾带领20XXCLUB全队参加2020年的一场音乐节,这也是20XXCLUB首次全员合照,王嗣尧Turbo、陈彦希Regi、<em>Mac</em> Ova Seas、林渝植等人全部出场。然而让
2021-06-01 09:31:34
目前应用IPFS的机构:1 谷歌<em>浏览器</em>支持IPFS分布式协议 2 万维网 (历史档案博物馆)数据库 3 火狐<em>浏览器</em>支持 IPFS分布式协议 4 EOS 等数字货币数据存储 5 美国国会图书馆,历史资料永久保存在 IPFS 6 加
2021-06-01 09:31:24
开拓者的车机是兼容苹果和<em>安卓</em>,虽然我不怎么用,但确实兼顾了我家人的很多需求:副驾的门板还配有解锁开关,有的时候老婆开车,下车的时候偶尔会忘记解锁,我在副驾驶可以自己开门:第二排设计很好,不仅配置了一个很大的
2021-06-01 09:30:48
不仅是<em>安卓</em>手机,苹果手机的降价力度也是前所未有了,iPhone12也“跳水价”了,发布价是6799元,如今已经跌至5308元,降价幅度超过1400元,最新定价确认了。iPhone12是苹果首款5G手机,同时也是全球首款5nm芯片的智能机,它
2021-06-01 09:30:45