溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

并發(fā)編程專題(二)-線程的創(chuàng)建方式

發(fā)布時間:2020-07-16 20:08:24 來源:網(wǎng)絡(luò) 閱讀:223 作者:BRUCELIU9527 欄目:編程語言
1.創(chuàng)建多線程幾種方式
1.1 繼承Thread,重寫父類的run()方法

Java使用java.lang.Thread類代表線程,所有的線程對象都必須是Thread類或其子類的實例。每個線程的作用是完成一定的任務(wù),實際上就是執(zhí)行一段程序流即一段順序執(zhí)行的代碼。Java使用線程執(zhí)行體來代表這段程序流。Java中通過繼承Thread類來創(chuàng)建啟動多線程的步驟如下:

  1. 定義Thread類的子類,并重寫該類的run()方法,該run()方法的方法體就代表了線程需要完成的任務(wù),因此把run()方法稱為線程執(zhí)行體。
  2. 創(chuàng)建Thread子類的實例,即創(chuàng)建了線程對象
  3. 調(diào)用線程對象的start()方法來啟動該線程

代碼如下:

  • 自定義線程類

    /**
    * @author bruceliu
    * @create 2019-05-29 23:15
    * @description 繼承Thread,重寫父類的run()方法
    */
    public class MyThread extends Thread {
    
    /*
     * 利用繼承中的特點
     * 將線程名稱傳遞 進行設(shè)置
     */
    public MyThread(String name) {
        super(name);
    }
    
    /*
     * 重寫run方法
     * 定義線程要執(zhí)行的代碼
     */
    public void run() {
        for (int i = 0; i < 20; i++) {
            //getName()方法 來自父親
            System.out.println(getName() + i);
        }
    }
    }
  • 測試類

    /**
    * @author bruceliu
    * @create 2019-05-29 23:17
    * @description 繼承Thread,重寫父類的run()方法
    */
    public class TestMyThread {
    
    public static void main(String[] args) {
        System.out.println("這里是main線程");
        MyThread mt = new MyThread("小強");
        mt.start();//開啟了一個新的線程
        for (int i = 0; i < 20; i++) {
            System.out.println("旺財:"+i);
        }
    }
    }
  • 流程圖
    并發(fā)編程專題(二)-線程的創(chuàng)建方式

有可能有些人看不到這么明顯的效果,這也很正常。所謂的多線程,指的是兩個線程的代碼可以同時運行,而不必一個線程需要等待另一個線程內(nèi)的代碼執(zhí)行完才可以運行。對于單核CPU來說,是無法做到真正的多線程的,每個時間點上,CPU都會執(zhí)行特定的代碼,由于CPU執(zhí)行代碼時間很快,所以兩個線程的代碼交替執(zhí)行看起來像是同時執(zhí)行的一樣。那具體執(zhí)行某段代碼多少時間,就和分時機制系統(tǒng)有關(guān)了。分時系統(tǒng)把CPU時間劃分為多個時間片,操作系統(tǒng)以時間片為單位片為單位各個線程的代碼,越好的CPU分出的時間片越小。所以看不到明顯效果也很正常,一個線程打印5句話本來就很快,可能在分出的時間片內(nèi)就執(zhí)行完成了。所以,最簡單的解決辦法就是把for循環(huán)的值調(diào)大一點就可以了(也可以在for循環(huán)里加Thread.sleep方法,這個之后再說)。

1.2 實現(xiàn)Runnable接口,重寫run方法

采用 java.lang.Runnable 也是非常常見的一種,我們只需要重寫run方法即可。和繼承自Thread類差不多,不過實現(xiàn)Runnable后,還是要通過一個Thread來啟動:

  1. 定義Runnable接口的實現(xiàn)類,并重寫該接口的run()方法,該run()方法的方法體同樣是該線程的線程執(zhí)行體。
  2. 創(chuàng)建Runnable實現(xiàn)類的實例,并以此實例作為Thread的target來創(chuàng)建Thread對象,該Thread對象才是真正
    的線程對象。
  3. 調(diào)用線程對象的start()方法來啟動線程。

代碼如下:

  • 自定義線程類
    /**
    * @author bruceliu
    * @create 2019-05-30 11:37
    * @description 實現(xiàn)Runnable接口,重寫run方法
    */
    public class MyRunnable implements Runnable{
    @Override
    public void run() {
        for (int i = 0; i < 20; i++) {
            System.out.println(Thread.currentThread().getName()+" "+i);
        }
    }
    }
  • 線程測試類

    /**
    * @author bruceliu
    * @create 2019-05-30 11:39
    * @description 實現(xiàn)Runnable接口,重寫run方法
    */
    public class TestMyRunnable {
    
    public static void main(String[] args) {
        //創(chuàng)建自定義類對象 線程任務(wù)對象
        MyRunnable mr = new MyRunnable();
        //創(chuàng)建線程對象
        Thread t = new Thread(mr, "小強");
        t.start();
        for (int i = 0; i < 20; i++) {
            System.out.println("旺財 " + i);
        }
    }
    }

    通過實現(xiàn)Runnable接口,使得該類有了多線程類的特征。run()方法是多線程程序的一個執(zhí)行目標。所有的多線程代碼都在run方法里面。Thread類實際上也是實現(xiàn)了Runnable接口的類。
    在啟動的多線程的時候,需要先通過Thread類的構(gòu)造方法Thread(Runnable target) 構(gòu)造出對象,然后調(diào)用Thread對象的start()方法來運行多線程代碼。
    實際上所有的多線程代碼都是通過運行Thread的start()方法來運行的。因此,不管是繼承Thread類還是實現(xiàn)Runnable接口來實現(xiàn)多線程,最終還是通過Thread的對象的API來控制線程的,熟悉Thread類的API是進行多線程編程的基礎(chǔ)。

    Runnable對象僅僅作為Thread對象的target,Runnable實現(xiàn)類里包含的run()方法僅作為線程執(zhí)行體。而實際的線程對象依然是Thread實例,只是該Thread線程負責執(zhí)行其target的run()方法。

1.3 使用匿名內(nèi)部類方式

使用線程的內(nèi)匿名內(nèi)部類方式,可以方便的實現(xiàn)每個線程執(zhí)行不同的線程任務(wù)操作。
使用匿名內(nèi)部類的方式實現(xiàn)Runnable接口,重新Runnable接口中的run方法:

  • 代碼如下:

    /**
    * @author bruceliu
    * @create 2019-05-30 11:55
    * @description 使用匿名內(nèi)部類方式
    */
    public class NoNameInnerClassThread {
    
    public static void main(String[] args) {
        System.out.println("-----多線程創(chuàng)建開始-----");
        //‐‐‐這個整體 相當于new MyRunnable()
        Runnable r = new Runnable(){
            public void run(){
                for (int i = 0; i < 20; i++) {
                    System.out.println("張宇:"+i);
                }
            }
        };
        new Thread(r).start();
        for (int i = 0; i < 20; i++) {
            System.out.println("費玉清:"+i);
        }
        System.out.println("-----多線程創(chuàng)建結(jié)束-----");
    }
    }
    1.4.Thread和Runnable的區(qū)別

    如果一個類繼承Thread,則不適合資源共享。但是如果實現(xiàn)了Runable接口的話,則很容易的實現(xiàn)資源共享。
    總結(jié):

  • 實現(xiàn)Runnable接口比繼承Thread類所具有的優(yōu)勢:
    1. 適合多個相同的程序代碼的線程去共享同一個資源。
    2. 可以避免java中的單繼承的局限性。
    3. 增加程序的健壯性,實現(xiàn)解耦操作,代碼可以被多個線程共享,代碼和線程獨立。
    4. 線程池只能放入實現(xiàn)Runable或Callable類線程,不能直接放入繼承Thread的類。

在java中,每次程序運行至少啟動2個線程。一個是main線程,一個是垃圾收集線程。因為每當使用
java命令執(zhí)行一個類的時候,實際上都會啟動一個JVM,每一個JVM其實在就是在操作系統(tǒng)中啟動了一個進程。

2.Thread類

我們已經(jīng)可以完成最基本的線程開啟,那么在我們完成操作過程中用到了 java.lang.Thread 類,
API中該類中定義了有關(guān)線程的一些方法,具體如下:

  • 構(gòu)造方法:
    public Thread() :分配一個新的線程對象。
    public Thread(String name) :分配一個指定名字的新的線程對象。
    public Thread(Runnable target) :分配一個帶有指定目標新的線程對象。
    public Thread(Runnable target,String name) :分配一個帶有指定目標新的線程對象并指定名字。
  • 常用方法方法:
    public String getName() :獲取當前線程名稱。
    public void start() :導(dǎo)致此線程開始執(zhí)行; Java虛擬機調(diào)用此線程的run方法。
    public void run() :此線程要執(zhí)行的任務(wù)在此處定義代碼。
    public static void sleep(long millis) :使當前正在執(zhí)行的線程以指定的毫秒數(shù)暫停(暫時停止執(zhí)行)。
    public static Thread currentThread() :返回對當前正在執(zhí)行的線程對象的引用
    3.守護線程

    在Java中有兩類線程:User Thread(用戶線程)、Daemon Thread(守護線程)
    用個比較通俗的比如,任何一個守護線程都是整個JVM中所有非守護線程的保姆:只要當前JVM實例中尚存在任何一個非守護線程沒有結(jié)束,守護線程就全部工作;只有當最后一個非守護線程結(jié)束時,守護線程隨著JVM一同結(jié)束工作。Daemon的作用是為其他線程的運行提供便利服務(wù),守護線程最典型的應(yīng)用就是 GC (垃圾回收器),它就是一個很稱職的守護者。
    User和Daemon兩者幾乎沒有區(qū)別,唯一的不同之處就在于虛擬機的離開:如果 User Thread已經(jīng)全部退出運行了,只剩下Daemon Thread存在了,虛擬機也就退出了。 因為沒有了被守護者,Daemon也就沒有工作可做了,也就沒有繼續(xù)運行程序的必要了。

首先看下哪些是守護線程,哪些是非守護線程
并發(fā)編程專題(二)-線程的創(chuàng)建方式

值得一提的是,守護線程并非只有虛擬機內(nèi)部提供,用戶在編寫程序時也可以自己設(shè)置守護線程。下面的方法就是用來設(shè)置守護線程的。

Thread daemonTread = new Thread();  

  // 設(shè)定 daemonThread 為 守護線程,default false(非守護線程)  
 daemonThread.setDaemon(true);  

 // 驗證當前線程是否為守護線程,返回 true 則為守護線程  
 daemonThread.isDaemon();

代碼示例:

/**
 * @author bruceliu
 * @create 2019-06-01 17:46
 * @description 守護線程:進程線程(主線程掛了) 守護線程也會被自動銷毀.
 */
public class DaemonThread {

    public static void main(String[] args) {

        System.out.println("----------->主線程執(zhí)行開始......");

        Thread thread=new Thread(new Runnable() {
            @Override
            public void run() {
                while(true){
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    } finally {
                    }
                    System.out.println("我是子線程......");
                }
            }
        });

        //設(shè)置為守護線程
        thread.setDaemon(true);
        thread.start();

        for (int i = 0; i <10 ; i++) {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
            }
            System.out.println("我是主線程...");
        }
        System.out.println("----------->主線程執(zhí)行完畢......");

    }
}
向AI問一下細節(jié)

免責聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI