官术网_书友最值得收藏!

1.2 線程的創(chuàng)建、運行和設(shè)置

本節(jié)介紹如何使用Java API對線程進行基本的操作。與Java語言中的基本元素一樣,線程也是對象(Object)。在Java中,創(chuàng)建線程的方法有以下兩種。

? 直接繼承Thread類,然后重寫run()方法。

? 構(gòu)建一個實現(xiàn)Runnable接口的類并重寫run()方法,然后創(chuàng)建該類的實例對象,并以其作為構(gòu)造參數(shù)去創(chuàng)建Thread類的對象。建議首選這種方法,因為它可以帶來更多的擴展性。

在本節(jié)中,我們將采用第二種方法創(chuàng)建線程,然后學(xué)習(xí)如何改變線程的屬性。Thread類包含如下一些信息屬性,它們能夠輔助區(qū)分不同的線程、反映線程狀態(tài)、控制其優(yōu)先級等。

? ID:該屬性存儲了每個線程的唯一標識符。

? Name:該屬性存儲了線程的名字。

? Priority:該屬性存儲了Thread對象的優(yōu)先級。在Java 9中,線程優(yōu)先級的范圍為1~10,其中1表示最低優(yōu)先級,10表示最高優(yōu)先級。通常不建議修改線程的優(yōu)先級。線程優(yōu)先級僅供底層操作系統(tǒng)作為參考,不能保證任何事情,如果一定要修改,請知曉優(yōu)先級僅僅代表了一種可能性。

? Status:該屬性保存了線程的狀態(tài)。在Java中,線程有6種狀態(tài)——Thread. State枚舉中定義這些狀態(tài):NEW、RUNNABLE、BLOCKED、WAITING、TIMED_ WAITING和TERMINATED。這些狀態(tài)的具體意義如下。

? NEW:線程已經(jīng)創(chuàng)建完畢但未開始執(zhí)行。

? RUNNABLE:線程正在JVM中執(zhí)行。

? BLOCKED:線程處于阻塞狀態(tài),并且等待獲取監(jiān)視器。

? WAITING:線程在等待另一個線程。

? TIMED_WAITING:線程等待另一個線程一定的時間。

? TERMINATED:線程執(zhí)行完畢。

本節(jié)將在一個案例中創(chuàng)建10個線程來找出20000以內(nèi)的奇數(shù)。

項目準備

本案例是用Eclipse IDE實現(xiàn)的。如果開發(fā)者使用Eclipse或者其他IDE(例如NetBeans),則應(yīng)打開它并創(chuàng)建一個新的Java項目。

案例實現(xiàn)

根據(jù)如下步驟實現(xiàn)本案例。

1.創(chuàng)建一個名為Calculator的類,并實現(xiàn)Runnable接口:

        public class Calculator implements Runnable {

2.實現(xiàn)run()方法。在這個方法中,存放著線程將要運行的指令。在這里,這個方法用來計算20000以內(nèi)的奇數(shù):

        @Override
        public void run() {
          long current = 1L;
          long max = 20000L;
          long numPrimes = 0L;

          System.out.printf("Thread '%s': START\n",
                            Thread.currentThread().getName());
          while (current <= max) {
            if (isPrime(current)) {
              numPrimes++;
            }
            current++;
          }
          System.out.printf("Thread '%s': END. Number of Primes: %d\n",
                          Thread.currentThread().getName(), numPrimes);
        }

3.實現(xiàn)輔助方法isPrime()。該方法用于判斷一個數(shù)是否為奇數(shù):

        private boolean isPrime(long number) {
          if (number <= 2) {
            return true;
          }
          for (long i = 2; i < number; i++) {
            if ((number % i) == 0) {
              return false;
            }
          return true;
          }
        }

4.實現(xiàn)應(yīng)用程序的主方法,創(chuàng)建包含main()方法的Main類:

        public class Main {
                public static void main(String[] args) {

5.首先,輸出線程的最大值、最小值和默認優(yōu)先級:

        System.out.printf("Minimum Priority: %s\n",
                          Thread.MIN_PRIORITY);
        System.out.printf("Normal Priority: %s\n",
                          Thread.NORM_PRIORITY);
        System.out.printf("Maximun Priority: %s\n",
                          Thread.MAX_PRIORITY);

6.創(chuàng)建10個Thread對象,分別用來執(zhí)行10個Calculator任務(wù)。再創(chuàng)建兩個數(shù)組,用來保存Thread對象及其State對象。后續(xù)我們將用這些信息來查看線程的狀態(tài)。這里將5個線程設(shè)置為最大優(yōu)先級,另5個線程設(shè)置為最小優(yōu)先級:

        Thread threads[];
        Thread.State status[];
        threads = new Thread[10];
        status = new Thread.State[10];
        for (int i = 0; i < 10; i++) {
        threads[i] = new Thread(new Calculator());
          if ((i % 2) == 0) {
            threads[i].setPriority(Thread.MAX_PRIORITY);
          } else {
            threads[i].setPriority(Thread.MIN_PRIORITY);
          }
            threads[i].setName("My Thread " + i);
        }

7.接著將一些必要的信息保存到文件中,因此需要創(chuàng)建try-with-resources語句來管理文件。在這個代碼塊中,先將線程啟動前的狀態(tài)寫入文件,然后啟動線程:

        try (FileWriter file = new FileWriter(".\\data\\log.txt");
        PrintWriter pw = new PrintWriter(file);) {
          for (int i = 0; i < 10; i++) {
            pw.println("Main : Status of Thread " + i + " : " +
                        threads[i].getState());
            status[i] = threads[i].getState();
            }
            for (int i = 0; i < 10; i++) {
              threads[i].start();
            }

8.等待線程運行結(jié)束。在1.6節(jié)中,我們將用join()方法來等待線程結(jié)束。本案例中,由于我們需要記錄線程運行過程中狀態(tài)的轉(zhuǎn)變,因此不能使用join()方法來等待線程結(jié)束,而應(yīng)使用如下代碼:

            boolean finish = false;
            while (!finish) {
              for (int i = 0; i < 10; i++) {
                if (threads[i].getState() != status[i]) {
                  writeThreadInfo(pw, threads[i], status[i]);
                  status[i] = threads[i].getState();
              }
                }

              finish = true;
              for (int i = 0; i < 10; i++) {
                finish = finish && (threads[i].getState() ==
                                  State.TERMINATED);
              }
            }
          } catch (IOException e) {
            e.printStackTrace();
          }
        }

9.在上述代碼中,我們通過調(diào)用writeThreadInfo()方法來將線程信息記錄到文件中。代碼如下:

        private static void writeThreadInfo(PrintWriter pw,
                                              Thread thread,
                                              State state) {
          pw.printf("Main : Id %d - %s\n", thread.getId(),
                      thread.getName());
          pw.printf("Main : Priority: %d\n", thread.getPriority());
          pw.printf("Main : Old State: %s\n", state);
          pw.printf("Main : New State: %s\n", thread.getState());
          pw.printf("Main : ************************************\n");
        }

10.運行程序,然后觀察不同的線程是如何同時運行的。

結(jié)果分析

下圖是程序在控制臺的輸出,從中可以看到,線程正在并行處理各自的工作。

從下面的屏幕截圖中可以看到線程是如何創(chuàng)建的,擁有高優(yōu)先級的偶數(shù)編號線程比低優(yōu)先級的奇數(shù)編號線程優(yōu)先執(zhí)行。該截圖來自記錄線程狀態(tài)的log.txt文件。

每個Java應(yīng)用程序都至少有一個執(zhí)行線程。在程序啟動時,JVM會自動創(chuàng)建執(zhí)行線程運行程序的main()方法。

當調(diào)用Thread對象的start()方法時,JVM才會創(chuàng)建一個執(zhí)行線程。也就是說,每個Thread對象的start()方法被調(diào)用時,才會創(chuàng)建開始執(zhí)行的線程。

Thread類的屬性存儲了線程所有的信息。操作系統(tǒng)調(diào)度執(zhí)行器根據(jù)線程的優(yōu)先級,在某個時刻選擇一個線程使用CPU,并且根據(jù)線程的具體情況來實現(xiàn)線程的狀態(tài)。

如果沒有指定線程的名字,那么JVM會自動按照Thread-XX格式為線程命名,其中XX是一個數(shù)字。線程的ID和狀態(tài)是不可修改的,事實上,Thread類也沒有實現(xiàn)setId()和setStatus()方法,因為它們會引入對ID和狀態(tài)的修改。

一個Java程序?qū)⒃谒芯€程完成準確來說,是所有非守護線程完成。——譯者注后結(jié)束。初始線程(執(zhí)行main()方法的線程)完成,其他線程仍會繼續(xù)執(zhí)行直到完成。如果一個線程調(diào)用System.exit()命令去結(jié)束程序,那么所有線程將會終止各自的運行。

創(chuàng)建一個Thread對象并不意味著會創(chuàng)建一個新的執(zhí)行線程。同樣,調(diào)用實現(xiàn)Runnable接口類的run()方法也不會創(chuàng)建新的執(zhí)行線程。只有調(diào)用了start()方法,一個新的執(zhí)行線程才會真正創(chuàng)建。

其他說明

正如本節(jié)開頭所說,還有另一種創(chuàng)建執(zhí)行線程的方法——實現(xiàn)一個繼承Thread的類,并重寫其run()方法,創(chuàng)建該類的對象后,調(diào)用start()方法即可創(chuàng)建執(zhí)行線程。

可以使用Thread類的靜態(tài)方法currentThread()來獲取當前運行線程的Thread對象。

調(diào)用setPriority()方法時,需要對其拋出的IllegalArgumentException異常進行處理,以防傳入的優(yōu)先級不在合法范圍內(nèi)(1和10之間)。

參考閱讀

? 1.11節(jié)

主站蜘蛛池模板: 平邑县| 莱州市| 昌黎县| 博乐市| 广昌县| 克拉玛依市| 连云港市| 锦州市| 太康县| 灵台县| 敦化市| 龙门县| 石渠县| 长治市| 奉化市| 武鸣县| 万盛区| 鸡东县| 莱阳市| 巨野县| 新郑市| 乌拉特中旗| 淄博市| 明光市| 乐至县| 江口县| 武定县| 大港区| 祥云县| 郯城县| 肇东市| 铜山县| 百色市| 富宁县| 浏阳市| 明水县| 永平县| 兖州市| 珲春市| 雷山县| 廉江市|