Java多線程編程之創建子線程

一、基礎知識

學習線程之前先了解一下有關的基礎知識

(1)進程

一般可以在同一時間內執行多個程序的操作系統都有進程的概念。一個進程就是一個執行中的程序, 而每一個進程都有自己獨立的一塊內存空間、一組系統資源。在進程的概念中,每一個進程的內部數 據和狀態都是完全獨立的。

(2)線程

線程與進程相似,是一段完成某個特定功能的代碼,是程序中單個順序控制的流程,但與進程不同的 是,同類的多個線程是共享一塊內存空間和一組系統資源。所以系統在各個線程之間切換時,開銷要 比進程小的多,正因如此,線程被稱爲輕量級進程。一個進程中可以包含多個線程。

(3)主線程

Java程序至少會有一個線程,這就是主線程,程序啓動後是由JVM創建主線程,程序結束時由JVM停 止主線程。主線程它負責管理子線程,即子線程的啓動、掛起、停止等等操作。圖所示是進程、 主線程和子線程的關係,其中主線程負責管理子線程,即子線程的啓動、掛起、停止等操作。
在這裏插入圖片描述
獲取主線程示例代碼如下:

public class HelloWorld {
    public static void main(String[] args) {
//        獲取主線程
        Thread mainThread = Thread.currentThread();
        System.out.println("主線程名"+mainThread.getName());
    }
}

運行結果:

主線程名main

Thread.currentThread()獲得當前線程,由於在main()方法中當前線程就是主線程,
Thread是Java線程類,位於java.lang包中
getName()方法獲得線程的名字,主線程名是 main,由JVM分配。

二、創建子線程

Java中創建一個子線程涉及到:
java.lang.Thread類和java.lang.Runnable接口。
Thread是線程類,創建一 個Thread對象就會產生一個新的線程。
而線程執行的程序代碼是在實現Runnable接口對象的run()方法 中編寫的,實現Runnable接口對象是線程執行對象。
線程執行對象實現Runnable接口的run()方法,run()方法是線程執行的入口,該線程要執行程序代碼都 在此編寫的,run()方法稱爲線程體。

注意:主線程中執行入口是main(String[] args)方法,這裏可以控制程序的流程,管理其他的子線 程等。
子線程執行入口是線程執行對象(實現Runnable接口對象)的run()方法,在這個方法可以 編寫子線程相關處理代碼。

詳細瞭解java.lang.Thread類和java.lang.Runnable接口見Java官方API文檔
http://www.matools.com/api/java8

有以下幾種方式創建子線程

(1)通過實現Runnable接口

創建步驟:
1、通過實現Runnable接口創建線程執行類
2、通過重寫Runnable中的run方法,編寫線程執行代碼
3、創建線程Thread對象,將線程執行對象傳遞給它
4、開始線程

創建線程Thread對象時,可以將線程執行對象傳遞給它,這需要是使用Thread類如下兩個構造方法:

  • Thread(Runnable target, String name):target是線程執行對象,實現Runnable接口。name爲線程指 定一個名字。
  • Thread(Runnable target):target是線程執行對象,實現Runnable接口。線程名字是由JVM分配 的。

實現Runnable接口的線程執行對象Runner代碼如下:

public class Runner implements Runnable {
//    編寫執行線程的代碼,重寫Runnable接口中的run方法,run()方法是線程體,在該方法中編寫你自己的線程處理代碼。
    @Override
    public void run() {
        for (int i=0;i<10;i++){
//            打印線程次數和線程名
            System.out.printf("第%d次執行--%s\n",i,Thread.currentThread().getName());
            try {
//                隨機生成休眠時間
                long sleepTime = (long)(1000*Math.random());
//                線程休眠
                Thread.sleep(sleepTime);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
//        線程執行結束
        System.out.println("執行完成"+Thread.currentThread().getName());

    }
}

測試代碼:

public class HelloWorld {
    public static void main(String[] args) {
//        創建一個線程t1,參數是線程執行對象Runner
        Thread t1 = new Thread(new Runner());
        t1.start();
//        創建一個線程t2,參數是線程執行對象Runner
        Thread t2 = new Thread(new Runner(),"MyThread");
        t2.start();
    }
}

Thread.currentThread()可以獲得當前線程對象,getName()是Thread類的實例方法,可以獲得線程的名。
Thread.sleep(sleepTime)是休眠當前線程,sleep是靜態方法:

  • static void sleep(long millis):在指定的毫秒數內讓當前正在執行的線程休眠。
  • static void sleep(long millis, int nanos) 在指定的毫秒數加指定的納秒數內讓當前正在執行的線程 休眠。

線程創建完成還 需要調用start()方法才能執行,start()方法一旦調用線程進入可以執行狀態, 可以執行狀態下的線程等待CPU調度執行,CPU調用後線程進行執行狀態,運行run()方法。
運行結果如下:

0次執行--Thread-00次執行--MyThread
第1次執行--Thread-01次執行--MyThread
第2次執行--MyThread
第2次執行--Thread-03次執行--MyThread
第4次執行--MyThread
第3次執行--Thread-04次執行--Thread-05次執行--MyThread
第6次執行--MyThread
第5次執行--Thread-06次執行--Thread-07次執行--Thread-07次執行--MyThread
第8次執行--Thread-08次執行--MyThread
第9次執行--MyThread
第9次執行--Thread-0
執行完成Thread-0
執行完成MyThread

提示: 仔細分析一下運行結果,會發現兩個線程是交錯運行的,感覺就像是兩個線程在同時運行。但是實際上一臺PC通常就只有一顆CPU,在某個時刻只能是一個線程在運行,而Java語言在 設計時就充分考慮到線程的併發調度執行。對於程序員來說,在編程時要注意給每個線程執行的 時間和機會,主要是通過讓線程休眠的辦法(調用sleep()方法)來讓當前線程暫停執行,然後由 其他線程來爭奪執行的機會。如果上面的程序中沒有用到sleep()方法,則就是第一個線程先執行 完畢,然後第二個線程再執行完畢。所以用活sleep()方法是多線程編程的關鍵。

(2)通過繼承Thread線程類

事實上Thread類也實現了Runnable接口,所以Thread類也可以作爲線程執行對象,這需要繼承Thread類,覆蓋run()方法。

創建步驟:
1、通過繼承Thread線程類創建線程執行類
2、定義構造方法,通過super調用父類Thread構造方法
這兩個Thread類 構造方法:

  • Thread(String name):name爲線程指定一個名字。
  • Thread():線程名字是JVM分配的。

3、通過重寫Thread中的run方法,編寫線程執行代碼
4、創建線程執行對象,將參數傳遞給它
5、開始線程

代碼如下:

//線程執行對象
public class MyThread extends Thread {
//    調用了一個構造方法,通過super調用父類的構造方法
    public MyThread(){
        super();
    }
    public MyThread(String name){
        super(name);
    }
//   編寫執行線程代碼
//    重寫Threa的的run方法
    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            //            打印線程次數和線程名
            System.out.printf("第%d次執行--%s\n", i,getName());
            try {
                //                隨機生成休眠時間
                long sleepTime = (long) (1000 * Math.random());
                //                線程休眠
                Thread.sleep(sleepTime);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        //        線程執行結束
        System.out.println("執行完成" + getName());
    }
}

測試代碼:

public class HelloWorld {
    public static void main(String[] args) {
//        創建線程t1
        Thread t1 = new MyThread();
//        開始線程t1
        t1.start();
        //        創建線程t1
        Thread t2 = new MyThread("mythread");
//        開始線程t1
        t2.start();
    }
}

由於Java只支持單重繼承,繼承Thread類的方式不能再繼承其他父類。當開發一些圖形界 面的應用時,需要一個類既是一個窗口(繼承JFrame)又是一個線程體,那麼只能採用實現 Runnable接口方式。

(3)使用匿名內部類和Lambda表達式實現線程體

如果線程體使用的地方不是很多,可以不用單獨定義一個類。可以使用匿名內部類或Lambda表達式直接實現Runnable接口。Runnable中只有一個方法是函數式接口,可以使用Lambda表達式。
代碼如下:

//使用匿名內部類和Lambda表達式實現線程體
public class HelloWorld {
    public static void main(String[] args) {
//    創建線程t1,參數是實現Runnable接口的匿名內部類
        Thread t1 = new Thread(new Runnable() {
//            編寫執行線程代碼
            @Override
            public void run() {
                for (int i=0;i<10;i++){
//            打印線程次數和線程名
                    System.out.printf("第%d次執行--%s\n",i,Thread.currentThread().getName());
                    try {
//                隨機生成休眠時間
                        long sleepTime = (long)(1000*Math.random());
//                線程休眠
                        Thread.sleep(sleepTime);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                //        線程執行結束
                System.out.println("執行完成"+Thread.currentThread().getName());
            }
        });
//        開始線程t1

//        創建線程t2,參數是實現Runnable接口的Lambda表達式
        Thread t2 = new Thread(() -> { //重寫run方法
            for (int i=0;i<10;i++){
//            打印線程次數和線程名
                System.out.printf("第%d次執行--%s\n",i,Thread.currentThread().getName());
                try {
//                隨機生成休眠時間
                    long sleepTime = (long)(1000*Math.random());
//                線程休眠
                    Thread.sleep(sleepTime);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            //        線程執行結束
            System.out.println("執行完成"+Thread.currentThread().getName());
        },"MyThread");
        t2.start();
    }
}

匿名內部類和Lambda表達式不需要定義一個線程類文件,使用起來很方便。特別是 Lambda表達式使代碼變得非常簡潔。

以上內容僅供參考學習,如有侵權請聯繫我刪除!
如果這篇文章對您有幫助,左下角的大拇指就是對博主最大的鼓勵。
您的鼓勵就是博主最大的動力!

發佈了59 篇原創文章 · 獲贊 7 · 訪問量 2944
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章