1. 程式人生 > >Thread類相關方法

Thread類相關方法

thread類 CP 沒有效果 不能識別 run 可用 interrupt 一個 線程的創建

線程對象
每一個線程都是和類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類相關方法