Thread類相關方法
線程對象
每一個線程都是和類Thread的實例相關聯的。在Java中,有兩種基本的使用Thread對象的方式,可用來創建並發性程序。
1.在應用程序需要發起異步任務的時候,只要生成一個Thread對象即可(繼承Thread類和實現runnable接口),這樣可以直接控制線程的創建並對其進行管理。
2.把應用程序的任務交給執行器(executor),這樣可以將對線程的管理從程序中抽離出來。
Thread類中的靜態方法
Thread類中的靜態方法表示操作的線程是"正在執行靜態方法所在的代碼塊的線程"。為什麽Thread類中要有靜態方法,這樣就能對CPU當前正在運行的線程進行操作。下面來看一下Thread類中的靜態方法:
1、currentThread()
currentThread()方法返回的是對當前正在執行線程對象的引用。看一個重要的例子,然後得出結論:
public class MyThread04 extends Thread { static { System.out.println("靜態塊的打印:" + Thread.currentThread().getName()); } public MyThread04() { System.out.println("構造方法的打印:" + Thread.currentThread().getName()); }public void run() { System.out.println("run()方法的打印:" + Thread.currentThread().getName()); } } public static void main(String[] args) { MyThread04 mt = new MyThread04(); mt.start(); }
看一下運行結果:
靜態塊的打印:main
構造方法的打印:main
run()方法的打印:Thread-0
這個例子說明了,線程類的構造方法、靜態塊是被main線程調用的,而線程類的run()方法才是應用線程自己調用的
1、睡眠 (public static native void sleep(long millis) throws InterruptedException;)(是Thread類的靜態方法)
Thread.sleep(long millis)和Thread.sleep(long millis, int nanos)靜態方法強制當前正在執行的線程休眠(暫停執行),以“減慢線程”。該方法的作用是在指定的毫秒內讓當前"正在執行的線程"休眠(暫停執行)。這個"正在執行的線程"是關鍵,指的是Thread.currentThread()返回的線程。根據JDK API的說法,"該線程不丟失任何監視器的所屬權",簡單說就是sleep代碼上下文如果被加鎖了,鎖依然在,但是CPU資源會讓出給其他線程。
線程睡眠的原因:線程執行太快,或者需要強制進入下一輪,因為Java規範不保證合理的輪換。
睡眠的實現:調用靜態方法。
try {
Thread.sleep(123);
} catch (InterruptedException e) {
e.printStackTrace();
}
睡眠的位置:為了讓其他線程有機會執行,可以將Thread.sleep()的調用放線程run()之內。這樣才能保證該線程執行過程中會睡眠。
註意:
1、線程睡眠是幫助所有線程獲得運行機會的最好方法。
2、線程睡眠到期自動蘇醒,並返回到可運行狀態,不是運行狀態。sleep()中指定的時間是線程不會運行的最短時間。因此,sleep()方法不能保證該線程睡眠到期後就開始執行。
3、sleep()是靜態方法,只能控制當前正在運行的線程。
2、線程的優先級和線程讓步yield() (是Thread類的靜態方法)
線程的讓步是通過Thread.yield()來實現的。yield()方法的作用是:暫停當前正在執行的線程對象,並執行其他線程。
要理解yield(),必須了解線程的優先級的概念。線程總是存在優先級,優先級範圍在1~10之間。JVM線程調度程序是基於優先級的搶先調度機制。在大多數情況下,當前運行的線程優先級將大於或等於線程池中任何線程的優先級。但這僅僅是大多數情況。
註意:當設計多線程應用程序的時候,一定不要依賴於線程的優先級。因為線程調度優先級操作是沒有保障的,只能把線程優先級作用作為一種提高程序效率的方法,但是要保證程序不依賴這種操作。
當線程池中線程都具有相同的優先級,調度程序的JVM實現自由選擇它喜歡的線程。這時候調度程序的操作有兩種可能:一是選擇一個線程運行,直到它阻塞或者運行完成為止。二是時間分片,為池內的每個線程提供均等的運行機會。
設置線程的優先級:線程默認的優先級是創建它的執行線程的優先級。可以通過setPriority(int newPriority)更改線程的優先級。例如:
Thread t = new MyThread();
t.setPriority(8);
t.start();
線程優先級為1~10之間的正整數,JVM從不會改變一個線程的優先級。然而,1~10之間的值是沒有保證的。一些JVM可能不能識別10個不同的值,而將這些優先級進行每兩個或多個合並,變成少於10個的優先級,則兩個或多個優先級的線程可能被映射為一個優先級。
線程默認優先級是5,Thread類中有三個常量,定義線程優先級範圍:
static int MAX_PRIORITY
線程可以具有的最高優先級。
static int MIN_PRIORITY
線程可以具有的最低優先級。
static int NORM_PRIORITY
分配給線程的默認優先級。
3、Thread.yield()方法
Thread.yield()方法作用是:暫停當前正在執行的線程對象,並執行其他線程。
yield()應該做的是讓當前運行線程回到可運行狀態,以允許具有相同優先級的其他線程獲得運行機會。因此,使用yield()的目的是讓相同優先級的線程之間能適當的輪轉執行。但是,實際中無法保證yield()達到讓步目的,因為讓步的線程還有可能被線程調度程序再次選中。
結論:yield()從未導致線程轉到等待/睡眠/阻塞狀態。在大多數情況下,yield()將導致線程從運行狀態轉到可運行狀態,但有可能沒有效果。
看一下例子:
public class MyThread08 extends Thread { public void run() { long beginTime = System.currentTimeMillis(); int count = 0; for (int i = 0; i < 50000000; i++) { Thread.yield(); count = count + i + 1; } long endTime = System.currentTimeMillis(); System.out.println("用時:" + (endTime - beginTime) + "毫秒!"); } } public static void main(String[] args) { MyThread08 mt = new MyThread08(); mt.start(); } —————————————————————————————————————————————————————————————————————————————————————————————————— 結果: 用時:3264毫秒! 用時:3299毫秒! 用時:3232毫秒! 用時:3256毫秒! 用時:3283毫秒! 用時:3504毫秒! 用時:3378毫秒!
看到,每次執行的用時都不一樣,證明了yield()方法放棄CPU的時間並不確定。
4. isDaeMon、setDaemon(boolean on) (是Thread的實例方法)
講解兩個方法前,首先要知道理解一個概念。Java中有兩種線程,一種是用戶線程,一種是守護線程。守護線程是一種特殊的線程,它的作用是為其他線程的運行提供便利的服務,最典型的應用便是GC線程。如果進程中不存在非守護線程了,那麽守護線程自動銷毀,因為沒有存在的必要,為別人服務,結果服務的對象都沒了,當然就銷毀了。理解了這個概念後,看一下例子:
public class MyThread1 extends Thread { private int i = 0; public void run() { try { while (true) { i++; System.out.println("i = " + i); Thread.sleep(1000); } } catch (InterruptedException e) { e.printStackTrace(); } } } public static void main(String[] args) { try { MyThread1 mt = new MyThread1(); mt.setDaemon(true); mt.start(); Thread.sleep(5000); System.out.println("我離開thread對象再也不打印了,我停止了!"); } catch (InterruptedException e) { e.printStackTrace(); } } ———————————————————————————————— 結果: i = 1 i = 2 i = 3 i = 4 i = 5 我離開thread對象再也不打印了,我停止了! i = 6
要解釋一下。我們將MyThread1線程設置為守護線程,看到第6行的那句話,而i停在6不會再運行了。這說明,main線程運行了5秒多結束,而i每隔1秒累加一次,5秒後main線程執行完結束了,MyThread1作為守護線程,main函數都運行完了,自然也沒有存在的必要了,就自動銷毀了,因此也就沒有再往下打印數字。
關於守護線程,有一個細節註意下,setDaemon(true)必須在線程start()之前
Thread類相關方法