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