摘要:執行新線程的方式是調用該線程對象的方法。線程之間沒有父子關系線程與線程之間是平等的,并沒有父子關系。線程是并行執行的在示例中,其實是與主線程并行執行的。當它運行完畢時,我們就得到所有線程的執行結果了。
主線程
首先每個 Java 程序都是從主線程開始運行的,主線程就是執行 main() 方法的那個線程。在 main() 方法中獲取當前線程很簡單:
// 示例1 public static void main(String[] args) { Thread mainThread = Thread.currentThread(); System.out.println("當前線程: " + mainThread.getName()); }
Thread 對象的文檔在這里。Thread 對象包含很多方法和屬性,除了上面例子當中的 name 屬性外,還有狀態、優先級等等。現在我們只需要知道 main() 方法是在主線程中運行就可以了。
線程是可以暫停的我們通常使用 sleep() 方法來使線程在指定的時間內暫停執行。下面是一個在主線程中執行循環和暫停的例子:
// 示例2 public static void main(String[] args) { for (int i = 0; i < 10; i++) { System.out.println(i); try { Thread.sleep(500); // 暫停線程 } catch (InterruptedException e) { e.printStackTrace(); } } }
注意:首先,sleep() 方法是靜態的,因為它永遠只能用在當前的線程上;其次,sleep() 方法會拋出異常,該異常通常發生在暫停狀態被打斷時。所以這里用 try-catch 代碼塊包圍起來。
sleep() 方法接受一個 long 類型的參數,指明需要暫停多少毫秒。這個例子當中,我們循環輸出 i 變量,共循環 10 次,每輸出一次就暫停 500 毫秒。
你可能覺得整個程序的運行時間會是精確的 5000 毫秒,但請千萬不要這么認為,首先 sleep() 方法并非十分精確,CPU 在各個線程之間切換會要花掉很微量的一點時間,如果這個例子循環次數不是 10 次而是十萬次百萬次,那么積累的誤差就會比較大了;其次,代碼中的 System.out.println() 方法和 for 循環本身也要花掉一點時間,所以每次循環不會是絕對精確的 500 毫秒。
創建新的線程除了主線程外,我們還可以創建和執行另外的線程。執行新線程的方式是調用該線程對象的 start() 方法。
// 示例3 public static void main(String[] args) { Thread thread1 = new Thread(); thread1.start(); // 啟動線程 }
從這個例子中我們可以看到,thread1 是一個通過 new Thread() 創建出來的對象。把線程看作是對象這點十分重要,這意味著我們可以創建 Thread 的子類,而子類的對象仍然是線程對象。執行這段代碼什么輸出都沒有,因為我們沒有為 thread1 定義要執行什么操作。下面的例子中,我們讓 thread1 來做循環輸出。
// 示例4 public static void main(String[] args) { Thread thread1 = new Thread() { @Override public void run() { // 指定線程要做的事 for (int i = 0; i < 10; i++) { System.out.println(i); try { Thread.sleep(500); // 暫停線程 } catch (InterruptedException e) { e.printStackTrace(); } } } }; thread1.start(); // 啟動線程 }
這里我們創建了一個 Thread 類的匿名子類,并覆寫了 run() 方法。通過覆寫 run() 方法,我們可以指定線程要做哪些事情。
線程是并行執行的線程之間沒有父子關系
線程與線程之間是平等的,并沒有父子關系。不過 Java 為了方便管理線程,定義了一個叫線程組(ThreadGroup)的類,線程組之間可以存在父子關系。不過這個概念平常用的很少,所以這里只是順帶提下,不作詳細介紹。
在示例4中,thread1 其實是與主線程并行執行的。為了演示這點,我們首先將這個循環提取成一個方法:
private static void printNumbers(int start, int end) { for (int i = start; i < end; i++) { System.out.println(i); try { Thread.sleep(500); // 暫停線程 } catch (InterruptedException e) { e.printStackTrace(); } } }
然后示例4就變成了:
public static void main(String[] args) { Thread thread1 = new Thread() { @Override public void run() { printNumbers(0, 10); // 提取出來的方法 } }; thread1.start(); // 啟動線程 }
我們在最后面添加一行,讓主線程在啟動 thread1 之后也做一個循環輸出:
// 示例5 public static void main(String[] args) { Thread thread1 = new Thread() { @Override public void run() { printNumbers(0, 10); // 循環輸出 } }; thread1.start(); // 啟動線程 printNumbers(100, 110); // 主線程也循環輸出 }
執行 main() 方法,在輸出中你可以看到 0~9 與 100~109 交替出現,這說明主線程和 thread1 在同時執行。
將線程中的邏輯獨立出來為了使線程中的邏輯能夠被重用,我們通常將其聲明為一個獨立的類。在前面的代碼示例中,我們都是以匿名類的方式來創建線程的。獨立聲明一個線程類的方式是這樣的:
// 示例6 public class MyThread extends Thread { private int start; private int end; // 構造方法 public MyThread(int start, int end) { this.start = start; this.end = end; } @Override public void run() { printNumbers(); } private void printNumbers() { for (int i = this.start; i < this.end; i++) { System.out.println(i); try { Thread.sleep(500); // 暫停線程 } catch (InterruptedException e) { e.printStackTrace(); } } } }
在示例 6 中我們可以看到,執行這個線程所需的兩個參數現在變成了 MyThread 的兩個成員。這是我們向線程傳遞執行參數的一般方式。提取成獨立的類之后,線程使用起來就非常簡單了:
public static void main(String[] args) { new MyThread(0,10).start(); new MyThread(100,110).start(); new MyThread(1000,1010).start(); }線程的返回值
我們有時候希望當線程執行完畢時,我們能得到一個結果。在示例 6 中我們了解了向線程傳遞參數的方式,類似的我們也可以為線程類定義一個成員用來保存線程的執行結果。下面是一個例子:
// 示例7 public class ThreadWithReturnValue extends Thread { public String result; @Override public void run() { try { Thread.sleep(3000); this.result = "result"; // 假設產生結果需要比較長的時間 } catch (InterruptedException e) { e.printStackTrace(); } } public static void main(String[] args) throws Exception { ThreadWithReturnValue thread = new ThreadWithReturnValue(); thread.start(); Thread.sleep(3100); System.out.println(thread.result); // 獲得結果 } }
在這個例子當中,ThreadWithReturnValue 線程產生結果需要 3 秒鐘,那么主線程就需要等待 3 秒以上才能得到 "result",否則就只能得到 null。在實際情況中,我們并不知道線程產生結果需要多長時間,而我們也不想無限制的等下去。
出于這樣的目的,Thread 對象為我們提供了 join() 方法,用于等待指定的線程直到執行完畢。示例 7 當中的 main() 方法可以改造成這樣子:
public static void main(String[] args) throws Exception { ThreadWithReturnValue thread = new ThreadWithReturnValue(); thread.start(); thread.join(); // 等待直到 thread 執行完畢 System.out.println(thread.result); }
這樣我們就能在線程執行完畢時立刻得到結果了。我們可以運行多個線程,然后依次調用它們的 join() 方法,這樣等待的時間就是它們當中運行最久的那個線程的運行時間。當它運行完畢時,我們就得到所有線程的執行結果了。線程的入門概念就介紹到這里,本文只是介紹非常基本的概念,Java 在處理多線程和并發方面還有很多很復雜的東西等待你去了解和嘗試。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/64182.html
摘要:本文是作者自己對中線程的狀態線程間協作相關使用的理解與總結,不對之處,望指出,共勉。當中的的數目而不是已占用的位置數大于集合番一文通版集合番一文通版垃圾回收機制講得很透徹,深入淺出。 一小時搞明白自定義注解 Annotation(注解)就是 Java 提供了一種元程序中的元素關聯任何信息和著任何元數據(metadata)的途徑和方法。Annotion(注解) 是一個接口,程序可以通過...
摘要:哪吒社區技能樹打卡打卡貼函數式接口簡介領域優質創作者哪吒公眾號作者架構師奮斗者掃描主頁左側二維碼,加入群聊,一起學習一起進步歡迎點贊收藏留言前情提要無意間聽到領導們的談話,現在公司的現狀是碼農太多,但能獨立帶隊的人太少,簡而言之,不缺干 ? 哪吒社區Java技能樹打卡?【打卡貼 day2...
摘要:一個線程做完,并將數據刷新回主內存了,下一個線程才會啟動。聲明了的變量在被賦值之后,線程會立刻將值寫回主內存在讀取變量時,線程會到主內存去讀取變量的最新值。 一個例子: public class Counter { public static int count = 0; public synchronized static void inc() { ...
摘要:我的學習筆記匯總標簽筆記分為兩大部分和筆記內容主要是對一些基礎特性和編程細節進行總結整理,適合了解基礎語法,想進一步深入學習的人如果覺得不錯,請給,這也是對我的鼓勵,有什么意見歡迎留言反饋目錄基礎鞏固筆記反射基礎鞏固筆記泛型基礎鞏 我的java&javaweb學習筆記(匯總) 標簽: java [TOC] 筆記分為兩大部分:javase和javaweb javase javawe...
閱讀 3593·2021-11-23 09:51
閱讀 2795·2021-11-23 09:51
閱讀 676·2021-10-11 10:59
閱讀 1672·2021-09-08 10:43
閱讀 3223·2021-09-08 09:36
閱讀 3289·2021-09-03 10:30
閱讀 3293·2021-08-21 14:08
閱讀 2195·2021-08-05 09:59