Java線程中的常見方法(start方法和run方法)
start方法和run方法
$start()$方法用來啟動一個線程,這時此線程處于就緒(可運行)狀態(tài),并沒有運行,一旦得到$cpu$時間片,就開始執(zhí)行$run()$方法。而直接調(diào)用$run()$方法,僅僅只是調(diào)用了一個類里的方法,其本質(zhì)上還是在當前線程中執(zhí)行的,因此只有使用$start()$方法來調(diào)用$run()$方法才能實現(xiàn)真正的多線程。
示例代碼
@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();
}
}上述代碼是直接調(diào)用的$run()$方法??梢钥吹酱蛴⌒畔⒗铮?main$線程執(zhí)行了這個方法。
@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$線程執(zhí)行的$run$方法。
注意
需要注意的是,當$Thread$對象調(diào)用了$start()$方法后,就會進入就緒狀態(tài),處于就緒狀態(tài)時無法再調(diào)用$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();
}
}異常信息:

sleep方法與yield方法
sleep
- 調(diào)用$sleep()$方法會讓當前線程從$Running$狀態(tài)變成$Time Waiting$狀態(tài)(阻塞)
- 其它線程可以使用$interrupt$方法打斷正在睡眠的線程,此時$sleep$方法會拋出InterruptedException
- 睡眠結(jié)束后的線程未必會立刻得到執(zhí)行
- 建議用$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$線程,此時打印線程的狀態(tài)應該是處于$RUNNABLE$狀態(tài),而讓主線程休眠是防止主線程先執(zhí)行打印,但是還未進入到$sleep()$狀態(tài)。當執(zhí)行到$run()$里邊的$sleep$方法時,線程進入$TIMED WAITING$狀態(tài)
@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();
}
}執(zhí)行結(jié)果

上述代碼中,當$start$方法啟動后,$t1$線程進入睡眠狀態(tài),打印提示信息,睡眠時間為$2s$,在$main$線程中睡眠$1s$后打斷$t1$線程的睡眠,提示打斷信息,并且調(diào)用$interrupt()$方法,此時線程被打斷,拋出異常。

$TimeUnit$類中新增了以什么單位去睡眠,可讀性更好,但是本質(zhì)上沒區(qū)別,只是進行了單位換算
TimeUnit.SECONDS.sleep(1);//該語句作用是睡眠一秒
yield
調(diào)用$yield$會讓當前進程從$Running$進入到$Runnable$就緒狀態(tài),然后調(diào)度執(zhí)行其他線程具體的實現(xiàn)依賴于操作系統(tǒng)的任務調(diào)度器,(即當任務調(diào)度器中沒有其他任務時,即使讓出$cpu$,也會繼續(xù)執(zhí)行該線程)$sleep$執(zhí)行后是進入阻塞狀態(tài),此時睡眠時間不結(jié)束,就不會分配$cpu$給該線程,但是$yield$是進入就緒狀態(tài),即如果沒有其他線程需要執(zhí)行,那么還會給該線程分配時間片,這是$sleep$和$yield$的最大區(qū)別線程優(yōu)先級
線程優(yōu)先級
會提示調(diào)度器優(yōu)先調(diào)度該線程,但它僅僅是一個提示,調(diào)度器可以忽略他
如果$cpu$比較忙,那么優(yōu)先級高的會獲得更多的時間片,可$cpu$空閑時,優(yōu)先級幾乎沒有
sleep的應用-防止cpu占用100%
在沒有利用$cpu$來計算時,不要讓$while(true)$空轉(zhuǎn)浪費$cpu$,這時可以可以使用$yield$或者$sleep$來讓$cpu$的使用權交給其他程序
while (true) {
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
}可以使用$wait$或者條件變量達到類似的效果
不同的是后兩者都需要加鎖,并且需要相應的喚醒操作,一般適用于要進行同步的場景
$sleep$適用于無需鎖同步的場景
join方法
以下程序的打印結(jié)果:
@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("結(jié)束");
r = 10;
}
};
t.start();
log.debug("r的值是{}", r);
log.debug("結(jié)束");
}
}
因為主線程和$t1$線程是并行的,$t1$線程需要$1s$后才能計算出$r$的值,而主線程一開始就要打印出$r$的值,因此打印的值為0
解決方法:
在$t.start();$后邊加上$t.join();$即可。$join$的作用是等待某線程運行結(jié)束。
以調(diào)用方的角度來說,需要等待結(jié)果返回才能繼續(xù)執(zhí)行就是同步,不需要等待返回結(jié)果就能繼續(xù)執(zhí)行的就是異步。

因此$join$方法實際上是讓其同步執(zhí)行
有實效的等待
$join(毫秒)$方法里可以有一個參數(shù)是傳入等待的時間,如果線程執(zhí)行時間大于等待時間,則等待時間到了之后,就會停止等待。如果線程執(zhí)行時間小于等待時間,則線程執(zhí)行完畢之后,等待也會跟著結(jié)束。不會把設置的等待時間過完。
interrupt方法
打斷$sleep, wait, join$的線程,即打斷阻塞狀態(tài)的線程
打斷$sleep$的線程,會清空打斷狀態(tài)
@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());
}
}
打斷正常運行的線程,不會清空打斷狀態(tài)
因此我們可以在線程中判斷打斷標記,來決定是否被打斷,以及執(zhí)行被打斷之前的收尾工作。
@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$需要等待所有線程都運行結(jié)束,才會結(jié)束。有一種特殊的線程叫做守護線程,只要其他非守護線程運行結(jié)束了,即使守護線程的代碼沒有執(zhí)行完畢,也會強制結(jié)束。
@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("主線程結(jié)束");
}
}如果不把$t$設置為守護線程,則因為線程內(nèi)部的死循環(huán),導致程序不會結(jié)束運行。
到此這篇關于Java線程中的常見方法(start方法和run方法)的文章就介紹到這了,更多相關Java線程方法內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
Java Swing組件單選框JRadioButton用法示例
這篇文章主要介紹了Java Swing組件單選框JRadioButton用法,結(jié)合具體實例形式分析了Swing單選框JRadioButton的使用方法及相關操作注意事項,需要的朋友可以參考下2017-11-11
Mybatis-Plus實現(xiàn)多主鍵批量保存及更新詳情
這篇文章主要介紹了Mybatis-Plus實現(xiàn)多主鍵批量保存及更新詳情,文章通過圍繞主題展開詳細的內(nèi)容介紹,具有一定的參考價值,需要的小伙伴可以參考一下2022-09-09
在SpringBoot當中使用Thymeleaf視圖解析器的詳細教程
Thymeleaf是一款開源的模板引擎,它允許前端開發(fā)者使用HTML與XML編寫動態(tài)網(wǎng)頁,hymeleaf的主要特點是將表達式語言嵌入到HTML結(jié)構中,它支持Spring框架,使得在Spring MVC應用中集成非常方便,本文給大家介紹了在SpringBoot當中使用Thymeleaf視圖解析器的詳細教程2024-09-09
Servlet+JDBC實現(xiàn)登陸功能的小例子(帶驗證碼)
這篇文章主要介紹了Servlet+JDBC實現(xiàn)登陸功能的小例子(帶驗證碼),文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2020-06-06
Java的MyBatis框架中實現(xiàn)多表連接查詢和查詢結(jié)果分頁
這篇文章主要介紹了Java的MyBatis框架中實現(xiàn)多表連接查詢和查詢結(jié)果分頁,借助MyBatis框架中帶有的動態(tài)SQL查詢功能可以比普通SQL查詢做到更多,需要的朋友可以參考下2016-04-04
手把手教你在eclipse創(chuàng)建第一個java?web項目并運行
Eclipse是用來做開發(fā)的自由集成開發(fā)環(huán)境,這也是很多java程序員會使用的開發(fā)環(huán)境,所以可以使用eclipse創(chuàng)建javaweb項目,下面這篇文章主要給大家介紹了關于如何在eclipse創(chuàng)建第一個java?web項目并運行的相關資料,需要的朋友可以參考下2023-02-02

