Java超詳細講解多線程中的Process與Thread
進程和線程的關(guān)系
?在操作系統(tǒng)中運行的程序就是進程,比如說QQ,播放器,游戲等等…程序是指令和數(shù)據(jù)的有序集合,其本身沒有任何運行的含義,是一個靜態(tài)的概念.
?進程和線程都是為了處理并發(fā)編程這樣的場景,但是進程有問題,頻繁拆功創(chuàng)建和釋放資源的時候效率低,相比之下,線程更輕量,創(chuàng)建和釋放效率更高.
?進程具有獨立性,每個進程有各自獨立的虛擬地址空間,一個進程掛了,不會影響其他進程,同一個進程中的多個線程,共用同一個內(nèi)存空間,一個線程掛了,可能影響其他的線程,甚至導(dǎo)致整個進程崩潰…
?而進程則是執(zhí)行程序的一次執(zhí)行過程,他是一個動態(tài)的概念,是系統(tǒng)資源分配的單位
?通常在一個進程中可以包含多個線程(如果把進程想象成一個工廠,那么線程就是工廠里的生產(chǎn)線,一個工廠里面面可以有一個生產(chǎn)線,也可以有多個生產(chǎn)線),當(dāng)然一個進程中至少一個線程,不然沒有存在的意義,線程是CPU調(diào)度和執(zhí)行的單位.
?真正的多線程是指有多個CPU,即多核.
?線程就是獨立執(zhí)行的路徑
?在程序執(zhí)行時,即使沒有自己創(chuàng)建線程,后臺也會有多個線程,如主線程,GC線程.
?每個線程在自己的工作內(nèi)存交互,內(nèi)存操控不當(dāng)會造成數(shù)據(jù)不一致.
操作系統(tǒng)是如何管理進程的
1.先描述一個進程.(明確除一個進程上面的一些相關(guān)屬性)
2.在組織若干個進程.(使用一些數(shù)據(jù)結(jié)構(gòu),把很多描述進程的信息放到一起,方便進行增刪查改,典型的實現(xiàn),就是使用雙向鏈表把每個進程的PCB串起來).
并行和并發(fā)
并行:微觀上,兩個CPU核心,同步你是執(zhí)行兩個任務(wù)的代碼.
并發(fā):微觀上,一個CPU核心,先執(zhí)行一會任務(wù)1,在執(zhí)行一會任務(wù)2,在執(zhí)行一會兒任務(wù)3,只要切換足夠快,宏觀上看起來,就好像很多任務(wù)同時執(zhí)行一樣,
并發(fā)和并行,只有在微觀上可以區(qū)分,宏觀上區(qū)分不了,微觀上這里的區(qū)分都是都是操作西歐統(tǒng)自動調(diào)度的結(jié)果.正因為供觀賞區(qū)分不了并行和并發(fā),在寫代碼的時候不去區(qū)分,只是在研究操作系統(tǒng)進程調(diào)度的時候稍做區(qū)分,其它場景基本都是使用并發(fā)作為一個統(tǒng)稱來代替.
創(chuàng)建線程的方法
方法一: 通過Thread類創(chuàng)建線程,這是最簡單的方法,創(chuàng)建子類,繼承Thread類,并重寫run方法.
package thread; class MyThread extends Thread{ @Override public void run() { System.out.println("hello thread"); } } public class Demo1 { public static void main(String[] args) { Thread t = new MyThread(); t.start(); } }
方法二:
import java.util.Scanner; class MyThread2 extends Thread{ @Override public void run() { System.out.println("hello thread!"); try { Thread.sleep(1000); }catch (InterruptedException e){ e.printStackTrace(); } } } public class Demo2 { public static void main1(String[] args) { Thread t = new MyThread2(); t.start(); while (true) { System.out.println("hello main!"); try { Thread.sleep(1000); }catch (InterruptedException e){ e.printStackTrace(); } } }
方法三: 創(chuàng)建一個匿名內(nèi)部類,繼承自Thread類,同時重寫run方法,同時再new出這個匿名內(nèi)部類的實例.
import java.util.*; class MyRunnable implements Runnable { @Override public void run() { System.out.println("hello!"); } } public class Demo3 { public static void main1(String[] args) { Thread t = new Thread(new MyRunnable()); t.start(); }
方法四: 同樣使用了匿名內(nèi)部類.
public class Demo4 { public static void main(String[] args) { Thread t = new Thread(){ @Override public void run() { System.out.println("hello thread"); } }; t.start(); } }
方法五: 使用了lambda表達式.
import java.util.*; public class Demo5 { public static void main1(String[] args) { Thread t = new Thread(new Runnable() { @Override public void run() { System.out.println("hello thread"); } }); t.start(); }
串行執(zhí)行和并發(fā)執(zhí)行
public class Demo7 { private static final long count = 10_0000_0000; public static void serial(){ //吉利程序執(zhí)行時間 long beg = System.currentTimeMillis(); long a = 0; for (int i = 0; i < count; i++) { a++; } long b = 0; for (int i = 0; i < count; i++) { b++; } long end = System.currentTimeMillis(); System.out.println("消耗時間: "+ (end - beg) + "ms"); } public static void concurrency() throws InterruptedException { long beg = System.currentTimeMillis(); Thread t1 = new Thread(()->{ long a = 0; for (int i = 0; i < count; i++) { a++; } }); t1.start(); Thread t2 = new Thread(()->{ long b = 0; for (int i = 0; i < count; i++) { b++; } }); t2.start(); //此處布恩那個直接記錄結(jié)束時間,別忘了,現(xiàn)在這個時間戳的代碼是在main 線程中 //main t1 t2 是并發(fā)執(zhí)行關(guān)系,此處t1 t2 還沒執(zhí)行完呢,這里就開始記錄結(jié)束時間了,這顯然是不準確的. //正確做法應(yīng)該是讓main線程等地啊t1 t2跑完了,在記錄結(jié)束時間 //join 效果就是等待線程結(jié)束,t1.join 就是讓main 線程等待 t1 結(jié)束,t2.join讓main 線程等待t2結(jié)束 t1.join(); t2.join(); long end = System.currentTimeMillis(); System.out.println("消耗時間: "+ (end - beg)+ "ms"); } public static void main(String[] args)throws InterruptedException { // serial(); concurrency(); } }
Thread中的一次額重要方法
1.start
決定了系統(tǒng)中是不是真的創(chuàng)建出線程,run單純的是一個普通的方法,描述了任務(wù)的內(nèi)容,start則是一個特殊的方法,內(nèi)部會在系統(tǒng)中創(chuàng)建線程.
public class Demo9 { public static void main(String[] args) { Thread t = new Thread(()->{ while (true) { System.out.println("hello thread1"); try { Thread.sleep(1000); }catch (InterruptedException e){ e.printStackTrace(); } } }); t.start(); //t.run(); while (true) { System.out.println("hello main"); try { Thread.sleep(1000); }catch (InterruptedException e){ e.printStackTrace(); } } } }
run方法只是一個普通的方法,在main線程里面調(diào)用run,其實并沒有創(chuàng)建線程,這個循環(huán)仍是在main中執(zhí)行的.
中斷線程
中斷線程:讓一個線程停下來.
線程停下來的關(guān)鍵,是要讓對應(yīng)的run方法執(zhí)行完.
(1).可以手動設(shè)置一個標志位,來控制線程是否要執(zhí)行結(jié)束.
public class Demo10 { private static boolean isQuit = false; public static void main(String[] args) { Thread t = new Thread(()->{ while (!isQuit) { System.out.println("hello thread"); try { Thread.sleep(1000); }catch (InterruptedException e){ e.printStackTrace(); } } }); t.start(); //只要把這個isQuit 設(shè)為true ,此時這個循環(huán)就退出了,進一步run 就執(zhí)行完了,再進一步就是線程結(jié)束了 try{ Thread.sleep(5000); }catch (InterruptedException e){ e.printStackTrace(); } isQuit = true; System.out.println("終止t線程"); } }
(2).使用thread中內(nèi)置的一個標志位來進行判定.
public class Demo11 { public static void main(String[] args) { Thread t = new Thread(()->{ while(!Thread.currentThread().isInterrupted()){ System.out.println("hello thread"); try { Thread.sleep(1000); }catch (InterruptedException e){ e.printStackTrace(); //當(dāng)觸發(fā)異常之后,立即就退出循環(huán) break; } } }); t.start(); try{ Thread.sleep(5000); }catch (InterruptedException e){ e.printStackTrace(); } //在線程中 調(diào)用interrupt 方法 來終端這個線程 //t.interrupt 的意思是讓t線程被中斷!! t.interrupt(); } }
線程等待
多個線程之間,調(diào)度順序是不確定的,線程之間的執(zhí)行是按照調(diào)度器來安排的,這個過程可以視為無序或者隨機的,有些時候,我們需要控制線程的順序,線程等待就是其中一種手段,此處的線程等,主要控制線程結(jié)束的先后順.
join: 調(diào)用join的時候,那個線程調(diào)用的join,那個線程就會阻塞等待,等到對應(yīng)的線程執(zhí)行完畢為止(對應(yīng)線程的run執(zhí)行完).
public class Demo12 { public static void main(String[] args) { Thread t = new Thread(()->{ for (int i = 0; i < 5; i++) { System.out.println("hello thread"); try{ Thread.sleep(1000); }catch (InterruptedException e){ e.printStackTrace(); } } }); t.start(); //在主線程中可以使用一個等待操作,來等待t線程執(zhí)行結(jié)束 try{ t.join(10000); }catch (InterruptedException e){ e.printStackTrace(); } } }
線程休眠(sleep)
如果線程調(diào)用了sleep方法,這個PCB就會進入到阻塞隊列.
當(dāng)睡眠時間到了,系統(tǒng)就會把這個PCB從阻塞隊列挪回到就緒隊列.
實例:
public class Demo14 { public static void main(String[] args) throws InterruptedException { Thread t = new Thread(()->{ while (true) { //z這里啥都不能有 try{ Thread.sleep(1000); }catch (InterruptedException e){ e.printStackTrace(); } } }); t.start(); Thread.sleep(1000); System.out.println(t.getState()); } }
到此這篇關(guān)于Java超詳細講解多線程中的Process與Thread的文章就介紹到這了,更多相關(guān)Java Process與Thread內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
關(guān)于SpringBoot中Ajax跨域以及Cookie無法獲取丟失問題
這篇文章主要介紹了關(guān)于SpringBoot中Ajax跨域以及Cookie無法獲取丟失問題,本文具有參考意義,遇到相同或者類似問題的小伙伴希望可以從中找到靈感2023-03-03如何在 Java 中利用 redis 實現(xiàn) LBS 服務(wù)
基于位置的服務(wù),是指通過電信移動運營商的無線電通訊網(wǎng)絡(luò)或外部定位方式,獲取移動終端用戶的位置信息,在GIS平臺的支持下,為用戶提供相應(yīng)服務(wù)的一種增值業(yè)務(wù)。下面我們來一起學(xué)習(xí)一下吧2019-06-06Springboot整合第三方登錄功能的實現(xiàn)示例
本文主要介紹了Springboot整合第三方登錄功能的實現(xiàn)示例,文中通過示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下2022-01-01