免费视频淫片aa毛片_日韩高清在线亚洲专区vr_日韩大片免费观看视频播放_亚洲欧美国产精品完整版

打開APP
userphoto
未登錄

開通VIP,暢享免費電子書等14項超值服

開通VIP
【JAVA并發(fā)第二篇】Java線程的創(chuàng)建與運行,線程狀態(tài)與常用方法

1、線程的創(chuàng)建與運行

(1)、繼承或直接使用Thread類

繼承Thread類創(chuàng)建線程:

/**
 * 主類
 */
public class ThreadTest {
    public static void main(String[] args) {
        //創(chuàng)建線程對象
        My_Thread my_thread = new My_Thread();
        //啟動線程
        my_thread.start();
    }
}

/**
 * 繼承Thread
 */
class My_Thread extends Thread{
    @Override
    public void run(){  //線程的任務
        System.out.println("My_Thread Running");
    }
}

直接使用Thread類創(chuàng)建線程:

class ThreadTest02 {
    public static void main(String[] args) {
        //直接使用Thread創(chuàng)建線程,"My_Thread"是取得線程名
        Thread my_thread = new Thread("My_Thread"){
            @Override
            public void run() {  //線程的任務
                System.out.println("My_Thread Running");
            }
        };
        //啟動線程
        my_thread.start();
    }
}

以上的方式都是直接使用Thread類創(chuàng)建線程,并通過start方法啟動線程,但線程并不會立即執(zhí)行,它還需要等待CPU調(diào)度,只有線程獲得CPU控制權(quán),才算是真正在執(zhí)行。

直接使用Thread類的好處是:
方便傳參,可在子類里添加成員變量,通過set方式設置參數(shù)或通過構(gòu)造函數(shù)傳參

直接使用Thread類的缺點處是:
線程的創(chuàng)建和任務代碼冗余在一起。也可能由于繼承了Thread類,故無法再繼承其他類。任務無返回值。

(2)、使用Runnable接口的run方法

/**
 * 主類
 */
public class ThreadTest03 {
    public static void main(String[] args) {
        RunnableTask task = new RunnableTask();
        //創(chuàng)建線程,參數(shù)1 是任務對象; 參數(shù)2 是線程名字,推薦寫上
        Thread my_thread = new Thread(task,"My_Thread");
        //啟動線程
        my_thread.start();
    }
}
/**
 * Runable接口實現(xiàn)類
 */
class RunnableTask implements Runnable{
    @Override
    public void run(){  //線程的任務
        System.out.println("Thread Running");
    }
}

以上的方式是使用Runnable接口的run方法,該方式將任務代碼與線程的創(chuàng)建分離,這樣在多個線程具有相同任務時,就可以使用同一個Runnable接口實現(xiàn),同時該方式的Runnable的實現(xiàn)類也可以繼承其他的類。該方式更靈活,故推薦使用其來創(chuàng)建線程。

但其缺點也是任務無返回值。

(3)、使用FutureTask的方式


//創(chuàng)建任務類,類似于Runnable
public class CallerTask implements Callable<String> {
    @Override
    public String call() throws Exception {
        return "hello thread";
    }

    public static void main(String[] args) throws ExecutionException, InterruptedException {
        //創(chuàng)建任務對象
        FutureTask<String> futureTask = new FutureTask<>(new CallerTask());
        //啟動線程
        new Thread(futureTask,"My_Thread").start();
        //主線程等待"My_Thread"的任務執(zhí)行完畢,并返回結(jié)果
        String res = futureTask.get();
        System.out.println(res);
    }
}

上述代碼實現(xiàn)了Callable接口的call()方法。在main函數(shù)內(nèi)首先創(chuàng)建FutureTask對象(構(gòu)造函數(shù)為CallerTask的實例)。將創(chuàng)建的FutureTask對象作為任務,并放到新創(chuàng)建的線程中啟動。運行完畢后,則可以使用get方法等待線程里的任務執(zhí)行完畢并返回結(jié)果。

2、Java線程的狀態(tài)

Java線程在其生命周期中可能有六種狀態(tài)。根據(jù)Java.lang.Thread類中的枚舉類型State的定義,其狀態(tài)有以下六種:

①NEW:初始狀態(tài),線程已被創(chuàng)建但還未調(diào)用start()方法來進行啟動。

②RUNNABLE:運行狀態(tài),調(diào)用start方法后,線程處于該狀態(tài)。注意,Java線程的運行狀態(tài),實際上包含了操作系統(tǒng)中的就緒狀態(tài)(已獲得除CPU外的一切運行資源,正在等待CPU調(diào)度,獲得CPU控制權(quán))和運行狀態(tài)(獲得CPU控制權(quán),線程真正在執(zhí)行)。因此,即使Java中的線程處于RUNNABLE狀態(tài),也并不意味著該線程就一定正在執(zhí)行(獲得CPU的控制權(quán)),該線程也有可能在等待CPU調(diào)度。

③BLOCKED:阻塞狀態(tài),線程阻塞于鎖,即線程在鎖的競爭中失敗,則處于阻塞狀態(tài)。

④WAITING:等待狀態(tài),該狀態(tài)的線程需要等待其他線程的中斷或通知。

⑤TIME-WAITING:超時等待狀態(tài),該狀態(tài)下的線程也在等待通知,但若在限定時間內(nèi)沒有,其他線程進行通知,那么超過規(guī)定時間的線程就會自動“醒來”,繼續(xù)執(zhí)行run方法內(nèi)的代碼。

⑥TERMINATED:終止狀態(tài),線程執(zhí)行完畢或者線程在執(zhí)行過程中拋出異常,則線程結(jié)束,線程處于終止狀態(tài)。

阻塞狀態(tài)(BLOCKED),是因為其在鎖競爭中失敗而在等待獲得鎖,而等待狀態(tài)(WAITING)則是在等待某一事件的發(fā)生,常見的如等待其他線程的通知或者中斷。

3、Java線程Thread類常用方法

(1)、start方法

是否為static方法:否。
作用:啟動一個新線程,在新線程調(diào)用run方法。

說明:線程調(diào)用start方法,進入運行狀態(tài)(RUNNABLE),但并不意味著線程中的代碼會立即執(zhí)行,因為Java線程中的運行狀態(tài)包含了操作系統(tǒng)層面的【就緒狀態(tài)】和【運行狀態(tài)】,所以只有Java線程真正獲得了CPU的控制權(quán),線程才能真正地在執(zhí)行。每個線程只能調(diào)用一次start方法來啟動線程,如果多次調(diào)用則會出現(xiàn)IllegalThreadStateException。

(2)、run方法

是否為static方法:否。
作用:線程啟動后會調(diào)用的方法。

說明:
①若使用繼承Thread類的方式創(chuàng)建線程,并重寫了run方法,則線程會在啟動后調(diào)用run方法,執(zhí)行其中的代碼。如果繼承時沒有重寫run方法或者run方法中沒有任何代碼,則該線程不會進行任何操作。
②若使用實現(xiàn)Runnable接口的方法創(chuàng)建線程,則在調(diào)用start啟動線程后,也會調(diào)用Runnable實現(xiàn)類中的run方法,如果沒有重寫,則默認不會進行任何操作。

那些run方法和start方法又有什么區(qū)別呢?

③start方法是真正能啟動一個新線程的方法,而run方法則是線程對象中的普通方法,即使線程沒有啟動,也可以通過線程對象來調(diào)用run方法,run方法并不會啟動一個新線程。

代碼如下:


public class StartAndRun{
    public static void main(String[] args) {
        //使用Thread創(chuàng)建線程
        Thread t = new Thread("my_thread"){ //為線程命名為"my_thread"
            @Override
            public void run() {
                //Thread.currentThread().getName():獲取當前線程的名字
                System.out.println("【"+Thread.currentThread().getName()+"】"+"線程中的run方法被調(diào)用");
                for (int i = 0; i < 3; i++) {
                    System.out.println(i);
                }
            }
        };
        //調(diào)用run方法
        t.run();
        //調(diào)用start方法
        t.start();
    }
}

其結(jié)果如下:

【main】線程中的run方法被調(diào)用
0
1
2
【my_thread】線程中的run方法被調(diào)用
0
1
2

可以看出在my_thread線程啟動前(調(diào)用start方法前),也可以調(diào)用線程對象t中的run方法,調(diào)用這個run方法的線程并不會是my_thread線程(因為還沒啟動呢),而是main方法所在的主線程main。這是因為run方法是作為線程對象的普通方法存在的,可以認為run方法中的代碼就是新線程啟動后所需要執(zhí)行的任務。如果通過線程對象調(diào)用run方法,那么在哪個線程調(diào)用的run方法,就由哪個線程負責執(zhí)行。

總的來說,Thread類的對象實例對應著操作系統(tǒng)實際存在的一個線程,該對象實例負責提供給用戶去操作線程、獲取線程信息。start方法會調(diào)用native修飾的本地方法start0,最終在操作系統(tǒng)中啟動一個線程,并會在本地方法中調(diào)用線程對象實例的run方法。所以,調(diào)用run方法并不會啟動一個線程,它只是作為線程對象等著被調(diào)用。

(3)、join方法

是否為static方法:否。
作用:用于同步,可以使用該方法讓線程之間的并行執(zhí)行變?yōu)榇袌?zhí)行。

有代碼如下:

/**
 * 主類
 */
public class Join {
    public static void main(String[] args) throws InterruptedException {
        Task task = new Task();
        Thread t1 = new Thread(task,"耗子尾汁");

        //啟動線程
        t1.start();

        //主線程打印
        for(int i = 0; i < 4; i++){
            if (i == 2) {
                //join方法:使main線程與t1線程同步執(zhí)行,即t1線程執(zhí)行完,main線程才會繼續(xù)
                t1.join();  
            }
            //Thread.currentThread().getName():獲取當前線程的名稱
            System.out.println("【"+Thread.currentThread().getName()+"】" + i); 
        }
    }
}

/**
 * Runnable接口實現(xiàn)類
 */
class Task implements Runnable{
    @Override
    public void run() {
        for(int i = 0; i < 3; i++){
            System.out.println("【"+Thread.currentThread().getName()+"】"+i);
        }
    }
}

其輸出如下:

【main】0
【main】1
【耗子尾汁】0
【耗子尾汁】1
【耗子尾汁】2
【耗子尾汁】3
【main】2
【main】3

在上面的代碼中,創(chuàng)建了一個命名為“耗子尾汁”的線程,并通過start方法啟動。主線程和“耗子尾汁”線程都有循環(huán)打印i的任務。在“耗子尾汁”線程啟動后,就會進入運行狀態(tài)(Runnable),等待CPU調(diào)度,以獲得CPU使用權(quán)來打印i。而主線程在執(zhí)行“耗子尾汁”線程的start方法后,就會繼續(xù)往下執(zhí)行,循環(huán)打印i。正常來講,主線程和“耗子尾汁”線程應該處于并行執(zhí)行的狀態(tài),即二者會各自執(zhí)行自己的for循環(huán)。但由于在主線程的for循環(huán)中調(diào)用了join方法,使得主線程交出了CPU的控制權(quán),并返回到“耗子尾汁”線程,等待該線程執(zhí)行完畢,主線程才繼續(xù)執(zhí)行。所以join方法就相當于在主線程中同步“耗子尾汁”線程,使“耗子尾汁”線程執(zhí)行完,才會繼續(xù)執(zhí)行主線程。其最終效果就是可以使用該方法讓線程之間的并行執(zhí)行變?yōu)榇袌?zhí)行。

join方法是可以傳參的。join(10)的意思就是,如果在A線程中調(diào)用了B線程.join(10),那么A線程就會同步等待B線程10毫秒,10毫秒后,A、B線程就會并行執(zhí)行。

同時也要注意,只有線程啟動了,調(diào)用join方法才有意義。在上述代碼中,如果“耗子尾汁”線程沒有調(diào)用start方法來啟動,那么join并不會起作用。

(4)、getId方法、getName方法、setName方法
是否為static方法:均為否。
作用:
①getId方法:獲取線程長整型的id、這個線程id是唯一的。
②getName方法:獲取線程名
③setName(String):設置線程名

(5)、getPriority方法、setPriority(int)方法
是否為static方法:均為否。
作用:
①setPriority(int)方法:設置線程的優(yōu)先級,優(yōu)先級的范圍為1-10。
②getPriority方法:獲取線程的優(yōu)先級。

現(xiàn)在的主流操作系統(tǒng)(windows、Linux等)基本都采用了時分的形式來調(diào)度運行線程,即將CPU的時間分為一個個時間片(這些時間片相等的),線程會得到若干時間片,時間片用完就會發(fā)生線程調(diào)度,并等待下一次的分配。線程優(yōu)先級就是決定線程需要多或者少分配一些時間片。

Java線程的優(yōu)先級范圍為1-10,默認優(yōu)先級為5。優(yōu)先級高的線程分配的時間片的數(shù)量要都多于優(yōu)先級低的線程??赏ㄟ^setPriority(int)方法來設置。頻繁阻塞的線程(比如I/O操作或休眠)的線程需要設置較高優(yōu)先級,而計算任務較重(比如偏向運算操作或需要較多CPU時間)的線程則設置較低優(yōu)先級,以避免CPU會被獨占。

需要注意的是,Java線程的優(yōu)先級設置只能給操作系統(tǒng)建議,并不能直接決定線程的調(diào)度,Java線程的調(diào)度只能由操作系統(tǒng)決定。操作系統(tǒng)完全可以忽略Java線程的優(yōu)先級設置。在不同的操作系統(tǒng)上Java線程的優(yōu)先級會存在差異,一些操作系統(tǒng)會直接無視優(yōu)先級的設置。所以一些在邏輯上有先后順序的操作,不能依靠設置Java線程的優(yōu)先級來完成。

Java子線程的默認優(yōu)先級與父線程的優(yōu)先級一致,例如在main方法中創(chuàng)建線程,那么主線程(默認為5)就是這個新線程的父線程,該新線程的默認優(yōu)先級為父線程的優(yōu)先級。如果給主線程設置優(yōu)先級為4,那么這個新線程的默認優(yōu)先級就為4。

(6)、getState()方法、isAlive()方法
是否為static方法:均為否。
作用:
①getState()方法:獲取線程的狀態(tài)(NEW、RUNNABLE、WATING、BLOCKED、TIME_WATING、TERMINATED)
②isAlive()方法:判斷線程是否存活,即是否線程已啟動但尚未終止((還沒有運行完
畢))。

(7)、interrupt()方法
是否為static方法:否。
作用:中斷線程,當A線程運行時,B線程可以通過A線程的對象實例來調(diào)用A線程的interrput()方法設置線程A的中斷標志位true,并立即返回。設置中斷僅僅是設置標志,通過設置中斷標志并不能直接終止該線程的執(zhí)行,而是被中斷的線程根據(jù)中斷狀態(tài)自行處理。如果打斷的是正在運行中的線程,那么該線程就會被設置中斷標志。但如果線程正在執(zhí)行sleep方法或者上面所說的join方法時,被調(diào)用了interrupt方法,那么這個被打斷的線程會拋出出 InterruptedException異常,并清除打斷標志。

(8)、interrupted()方法、isInterrupted()方法
是否為static方法:interrupted為static方法、isInterrupted為非static方法
作用:均為判斷線程是否被打斷。區(qū)別在于interrupted()方法會清除中斷標記,isInterrupted()方法不會清除中斷標志。

說明:在interrupted()方法內(nèi)部是獲取當前調(diào)用線程的中斷標志而不是調(diào)用interrupted()方法的實例對象的中斷標志。

(9)、sleep(long n)方法
是否為static方法:是。
作用:讓線程休眠,當一個執(zhí)行中的線程調(diào)用sleep方法后,該線程就會掛起,并把剩下的CPU時間片交給其他線程,但并不會直接指定由哪個線程占用,需要操作系統(tǒng)來進行調(diào)度。線程在休眠期間不參與CPU調(diào)度,但也不會把線程占有的其他資源(比如鎖)進行釋放。

需要注意的是,休眠時間到后線程也并不會直接繼續(xù)執(zhí)行,而是進入等待CPU調(diào)度的狀態(tài)。同時由于sleep方法是靜態(tài)方法,使用t.sleep()并不會讓t線程進入休眠,而是讓當前線程進入休眠(比如在main方法中調(diào)用t.sleep(),實際上是讓主線程進入休眠)。

(10)、yield() 方法
是否為static方法:是。
作用:使線程讓出CPU控制權(quán)。實際上該方法只是向操作系統(tǒng)請求讓出自己的CPU控制權(quán),但操作系統(tǒng)也可以選擇忽略。線程調(diào)用該方法讓出CPU控制權(quán)后,會進入就緒狀態(tài),也有可能遇到剛讓出CPU控制權(quán)后又被CPU調(diào)度執(zhí)行的情況。

本站僅提供存儲服務,所有內(nèi)容均由用戶發(fā)布,如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點擊舉報。
打開APP,閱讀全文并永久保存 查看更多類似文章
猜你喜歡
類似文章
Java多線程編程總結(jié)
java多線程
java線程概述 -- JR 精品文章
Java基礎-創(chuàng)建Java程序中的線程池
Java多線程學習(吐血超詳細總結(jié))
java多線程-概念&創(chuàng)建啟動&中斷&守護線程&優(yōu)先級&線程狀態(tài)(多線程編程之一)
更多類似文章 >>
生活服務
分享 收藏 導長圖 關(guān)注 下載文章
綁定賬號成功
后續(xù)可登錄賬號暢享VIP特權(quán)!
如果VIP功能使用有故障,
可點擊這里聯(lián)系客服!

聯(lián)系客服