執行緒相關方法一
宣告
版權宣告:本文參考CSDN博主「一個快樂的野指標~」的原創文章,僅用作個人學習,特此宣告
原文連結:https://blog.csdn.net/qq_44715943/article/details/116714584
執行緒方法
1. 獲取方法
1、方法概述
獲取當前執行緒物件、獲取執行緒物件名字、修改執行緒物件名字
方法名 | 作用 |
---|---|
static Thread currentThread() | 獲取當前執行緒物件 |
String getName() | 獲取執行緒物件名字 |
void setName(String name) | 修改執行緒物件名字 |
需要注意的是,當沒有設定執行緒名稱時,預設名稱為
- Thread-0
- Thread-1
- Thread-2
- Thread-3
- …
2、eg.
/** * 執行緒獲取方法測試 */ public class Demo { public static void main(String[] args) { Thread t = new Thread(new ThreadDemo()); //t.run(); // 啟動執行緒 t.start(); } } class ThreadDemo extends Thread { public void run(){ for(int i = 0; i < 2; i++){ // currentThread就是當前執行緒物件。當前執行緒是誰呢? // 當t1執行緒執行run方法,那麼這個當前執行緒就是t1 // 當t2執行緒執行run方法,那麼這個當前執行緒就是t2 Thread currentThread = Thread.currentThread(); //currentThread.setName("分支執行緒1"); System.out.println(currentThread.getName() + "-->" + i); System.out.println(super.getName() + "-->" + i); System.out.println(this.getName() + "-->" + i); } } }
很明顯直接呼叫run方法時當前執行緒為main執行緒
當用執行緒start方法呼叫run方法時,當前執行緒為分執行緒,其名稱被主動修改為"分支執行緒1"
3、發現了一個問題(待解決)
只有一個分支執行緒用於輸出0,1.為什麼該執行緒名稱為Thread-1而不是Thread-0呢?
2. sleep相關方法
1、sleep方法
方法名 | 作用 |
---|---|
static void sleep(long millis) | 讓當前執行緒休眠millis毫秒 |
- 靜態方法:Thread.sleep(1000);
- 引數是毫秒
- 作用: 讓當前執行緒進入休眠,進入“阻塞狀態”,放棄佔有CPU時間片,讓給其它執行緒使用。
這行程式碼出現在A執行緒中,A執行緒就會進入休眠。
這行程式碼出現在B執行緒中,B執行緒就會進入休眠。 - Thread.sleep()方法,可以做到這種效果:
間隔特定的時間,去執行一段特定的程式碼,每隔多久執行一次。
/**
* 用sleep方法實現每間隔1s時間列印一個數字
*/
public class SleepTest extends Thread{
public static void main(String[] args) {
//每間隔1s時間列印一個數字
for(int i = 0; i < 2; i++){
System.out.println(Thread.currentThread().getName() + "--->" + i);
// 睡眠1秒
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
2、中斷sleep的方法
方法名 | 作用 |
---|---|
void interrupt() | 終止執行緒的睡眠 |
/**
* 打斷sleep方法測試
*/
public class InterruptSleepTest {
public static void main(String[] args) {
Thread t = new Thread(new MyRunnable2());
t.setName("t");
t.start();
//執行緒休眠5s以後主動打斷休眠狀態
try {
Thread.sleep(1000 * 5);
} catch (InterruptedException e) {
e.printStackTrace();
}
//終斷t執行緒的睡眠(這種終斷睡眠的方式依靠了java的異常處理機制。)
t.interrupt();
}
}
class MyRunnable2 implements Runnable {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + "---> begin");
try {
// 睡眠1年
Thread.sleep(1000 * 60 * 60 * 24 * 365);
} catch (InterruptedException e) {
//e.printStackTrace();
}
//1年之後才會執行這裡
System.out.println(Thread.currentThread().getName() + "---> end");
}
}
分析上測試程式碼可知,本來run方法是要執行緒睡眠一年的時間才會輸出"當前執行緒名稱---> end"
但main方法中主動用interrupt();
方法打斷了執行緒的睡眠
需要注意的是這種終斷睡眠的方式依靠了java的異常處理機制
3. run方法的一個小知識點
為什麼run()方法只能try…catch…不能throws?
因為run()方法在父類中沒有丟擲任何異常,子類不能比父類丟擲更多的異常。
4. stop方法強行終止執行緒(不推薦)
1、eg.
/**
* 終止程式方法:stop方法測試
*/
public class StopTest {
public static void main(String[] args) {
Thread t = new Thread(new MyRunnable3());
t.setName("t");
t.start();
//模擬5秒:因為要在5s以後再強行終止t執行緒,所以要用sleep方法將stop方法延後5s
try {
Thread.sleep(1000 * 5);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 5秒之後強行終止t執行緒
t.stop(); // 已過時(不建議使用。)
}
}
class MyRunnable3 implements Runnable {
@Override
public void run() {
for(int i = 0; i < 10; i++){
System.out.println(Thread.currentThread().getName() + "--->" + i);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
2、需要注意的是
- 要在start方法啟動執行緒從而呼叫run方法之後先休眠5s再呼叫stop方法。
因為start方法只是一個啟動方法,它的執行和結束是一瞬間的事情,不會等待run方法執行完成後再結束
如果不將stop方法延後5s,它就會在start方法執行之後瞬間執行,run方法還沒開始就被強行終止了。如下圖,執行結果為空,很明顯run方法沒來得及執行
如下圖所示:在start方法和stop方法之間加入延後程式碼之後,實現了5秒之後強行終止t執行緒的目的,run方法只執行了5s也就是隻輸出了0~4就被強行終止了
2. 這種方式存在很大的缺點:容易丟失資料。因為這種方式是直接將執行緒殺死了,執行緒沒有儲存的資料將會丟失。不建議使用。
那麼,應該通過一個什麼樣的方法來合理地終止執行緒的執行呢?
引出以下內容
Java中合理結束一個程序的執行(常用)
1、eg.
/**
* 用合理的方法實現結束程序執行:加判斷
*/
public class RationalMethodTest {
public static void main(String[] args) {
MyRunable r = new MyRunable();
Thread t = new Thread(r);
t.setName("t");
t.start();
// 模擬5秒
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 終止執行緒
// 你想要什麼時候終止t的執行,那麼你把標記修改為false,就結束了。
r.run = false;
}
}
class MyRunable implements Runnable {
boolean run = true;
@Override
public void run() {
for (int i = 0; i < 10; i++){
if(run){
System.out.println(Thread.currentThread().getName() + "--->" + i);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}else{
// return就結束了,結束前未儲存的資料在這一步儲存,防止資料丟失
return;
}
}
}
}
需要注意:
2、為什麼if()語句要在迴圈裡面?
由於一個執行緒一直執行此程式,要是if判斷在外面只會在啟動執行緒時判斷並不會結束,因此需要每次迴圈判斷一下標記。
補充知識:執行緒排程
常見執行緒排程模型
-
搶佔式排程模型:
優先順序比較高的執行緒,其搶到的CPU時間片的概率就高一些/多一些。
java採用的就是搶佔式排程模型。 -
均分式排程模型:
平均分配CPU時間片。每個執行緒佔有的CPU時間片時間長度一樣。
平均分配,一切平等。
有一些程式語言,執行緒排程模型採用的是這種方式。
5. 執行緒排程相關方法
1、獲取和設定優先順序
獲取、設定執行緒優先順序
方法名 | 作用 |
---|---|
int getPriority() | 獲得執行緒優先順序 |
void setPriority(int newPriority) | 設定執行緒優先順序 |
- 最低優先順序1
- 預設優先順序是5
- 最高優先順序10
優先順序比較高的獲取CPU時間片可能會多一些。
常量名 | 備註 |
---|---|
static int MAX_PRIORITY | 最高優先順序(10) |
static int MIN_PRIORITY | 最低優先順序(1) |
static int NORM_PRIORITY | 預設優先順序(5) |
注意:main執行緒的預設優先順序是:5
eg.
/**
* 優先順序測試
*/
public class PriorityTest extends Thread{
public static void main(String[] args) {
System.out.println("最高優先順序為:" + Thread.MAX_PRIORITY);//最高優先順序:10
System.out.println("最低優先順序為:" + Thread.MIN_PRIORITY);//最低優先順序:1
System.out.println("預設優先順序為:" + Thread.NORM_PRIORITY);//預設優先順序:5
// main執行緒的預設優先順序是:5
System.out.println(currentThread().getName() + "執行緒的預設優先順序是:" + currentThread().getPriority());
Thread thread = new Thread(new MyRunnable());
thread.setPriority(10);
thread.start();
//thread.join();
// 優先順序較高的,只是搶到的CPU時間片相對多一些。
// 大概率方向更偏向於優先順序比較高的。
for(int i = 0; i < 2; i++){
System.out.println(Thread.currentThread().getName() + "-->" + i);
}
}
}
class MyRunnable implements Runnable {
@Override
public void run() {
for(int i = 0; i < 2; i++){
System.out.println(Thread.currentThread().getName() + "-->" + i);
}
}
}
發現了一個問題:為什麼優先順序低的main反而每次都能搶先執行?
- 其實,即使設定了執行緒的優先順序,一樣無法確保這個執行緒一定先執行,因為它有很大的隨機性。它並無法控制執行哪個執行緒,因為執行緒的執行,是搶佔資源後才能執行的操作,而搶點資源時,最多是給於執行緒優先順序較高的執行緒一點機會而已,能不能抓住可是不一定的
- 父執行緒main先啟動佔了CPU。如果要父執行緒後執行,那就在start()後加個join(),如下圖,此時併入的支線先執行,再執行當前程序main
2、讓位方法yield
1、概述
方法名 | 作用 |
---|---|
static void yield() | 讓位方法,當前執行緒暫停,回到就緒狀態,讓給其它執行緒。 |
yield()方法不是阻塞方法。讓當前執行緒讓位,讓給其它執行緒使用。
yield()方法的執行會讓當前執行緒從“執行狀態”回到“就緒狀態”。
注意:在回到就緒之後,有可能還會再次搶到CPU時間片。
2、eg.
/**
* 讓位方法yield測試
*/
public class YieldTest {
public static void main(String[] args) {
Thread t = new Thread(new MyRunnable1());
t.setName("t");
t.start();
for(int i = 1; i <= 1000; i++) {
System.out.println(Thread.currentThread().getName() + "--->" + i);
}
}
}
class MyRunnable1 implements Runnable {
@Override
public void run() {
for(int i = 1; i <= 1000; i++) {
//讓位
if(i % 100 == 0){
Thread.yield(); // 當前執行緒暫停一下,讓給主執行緒。
}
System.out.println(Thread.currentThread().getName() + "--->" + i);
}
}
}
3、觀察結果,發現了很多細節
-
無論優先順序的高低,一定是main先執行
因為程序先搶佔CPU,所以觀察上圖發現,當start方法呼叫了執行緒的run方法時,main方法已經運行了一段時間
當t執行緒run開始執行,main方法暫停
-
某個執行緒的讓位方法執行以後,不一定是立馬執行其他執行緒
因為yield()方法的執行會讓當前執行緒從“執行狀態”回到“就緒狀態”。此時的讓位執行緒是可以繼續搶奪CPU時間片的。觀察上圖發現,很明顯主執行緒main還沒有執行完畢,CPU時間片又被之前的讓位執行緒搶到了,讓位執行緒繼續執行直到下一次呼叫讓位方法才會再次讓位給主執行緒執行,如下圖所示
- 以上過程迴圈直到某一執行緒執行完畢打破該迴圈