第四阶段-多线程
多線程
(進(jìn)程和線程的區(qū)別)
根本區(qū)別: 進(jìn)程是操作系統(tǒng)資源分配的基本單位,線程是CPU調(diào)度和執(zhí)行的基本單位 環(huán)境區(qū)別: 在操作系統(tǒng)中有多個(gè)進(jìn)程同時(shí)運(yùn)行,在同一個(gè)進(jìn)程中又有多個(gè)線程同時(shí)執(zhí)行.在通過(guò)CPU的調(diào)度 下,在每個(gè)時(shí)間片中只有一個(gè)線程執(zhí)行 開(kāi)銷區(qū)別: 因?yàn)槊總€(gè)進(jìn)程都有獨(dú)立的代碼和數(shù)據(jù)空間,所以進(jìn)程之間的切換開(kāi)銷比較大.而線程可以看做一個(gè)輕量級(jí)的進(jìn)程,在同一類的線程中,他們的代碼和數(shù)據(jù)空間是共享的,所以線程之間的切換開(kāi)銷比較小. 內(nèi)存分配區(qū)別: 系統(tǒng)在運(yùn)行的時(shí)候會(huì)給每個(gè)進(jìn)程分配不同的內(nèi)存空間,而對(duì)于線程來(lái)說(shuō),除了CPU外,系統(tǒng)不會(huì)給線程分配內(nèi)存,線程之間只能共享資源. 包含關(guān)系: 線程是進(jìn)程的一部分.沒(méi)有線程的進(jìn)程可以看做是單線程的如果一個(gè)進(jìn)程有多個(gè)線程,則執(zhí)行的過(guò)程不是一條線,而是多條線程共同完成的.1.程序和進(jìn)程
1.程序和進(jìn)程的概念
程序: 就是硬盤上存儲(chǔ)的、靜止的代碼。
**進(jìn)程:**程序的一次執(zhí)行產(chǎn)生進(jìn)程。進(jìn)程是動(dòng)態(tài)向前的。
2.多任務(wù)操作系統(tǒng)的工作原理
假定 A,B,C 三個(gè)程序開(kāi)始運(yùn)行,A、B、C 產(chǎn)生進(jìn)程 Pa、Pb、Pc。Pa、Pb、Pc 排隊(duì)輪流使用 CPU。 假設(shè) Pa 先搶占到 CPU,Pa 執(zhí)行,執(zhí)行過(guò)程中 Pa 需要等待數(shù)據(jù)輸入(I/O 操作、請(qǐng)求網(wǎng)絡(luò)資源), 此時(shí) CPU 空轉(zhuǎn),為了提高 CPU 利用率,Pa 被切換出去,Pa 保存當(dāng)前執(zhí)行狀態(tài),Pa 掛起。Pb 搶占 CPU,Pb 開(kāi)始執(zhí)行,Pb 如果沒(méi)有數(shù)據(jù)輸入,Pb 也可能被切換出去(CPU 時(shí)間片到了),Pb 掛起;Pc 搶占到 CPU,開(kāi)始執(zhí)行,如果 Pc 有數(shù)據(jù)輸入,Pc 保存當(dāng)前狀態(tài)并掛起。此時(shí) 3 個(gè)進(jìn)程都掛起,CPU 空閑。CPU 挑選一個(gè)進(jìn)程運(yùn)行,根據(jù) CPU 執(zhí)行原則,選中 Pb,Pb 繼續(xù)執(zhí)行。 CPU 通過(guò)時(shí)間片實(shí)現(xiàn)多任務(wù),這樣的操作系統(tǒng)稱為多任務(wù)操作系統(tǒng),但同一時(shí)刻還是只有一個(gè)進(jìn)程執(zhí)行。3.并發(fā)和并行
**并發(fā):**在一段時(shí)間內(nèi)多個(gè)進(jìn)程輪流使用同一個(gè)CPU,多個(gè)進(jìn)程形成并發(fā)。
**并行:**在同一時(shí)刻多個(gè)進(jìn)程使用各自的CPU
2.線程
1.線程工作原理
線程的出現(xiàn)時(shí)為了解決實(shí)時(shí)性問(wèn)題. 線程是進(jìn)程的細(xì)分. 進(jìn)程被劃分為多個(gè)可以獨(dú)立運(yùn)行的線程,多個(gè)線程配合完成一個(gè)進(jìn)程的任務(wù). 總結(jié): [1] 線程再次提高了CPU的利用率; [2] 線程共享進(jìn)程資源,線程包含在進(jìn)程中; [3] 線程細(xì)分后稱為CPU調(diào)度的基本單位.進(jìn)程稱為操作系統(tǒng)資源分配的基本單位.3.實(shí)現(xiàn)線程的方式
1.繼承Thread類
// 繼承Thread類必須重寫run方法 public class AThread extends Thread{@Overridepublic void run() {for (int i = 0; i < 5;i++){System.out.println("AThread:" + i);}} }// 測(cè)試類 public class Test01 {public static void main(String[] args) {// 創(chuàng)建一個(gè)線程AAThread ta = new AThread();// 必須通過(guò)start開(kāi)啟線程,不能通過(guò)run,通過(guò)run就不是一個(gè)線程了.ta.start();// 在主線程中執(zhí)行一段代碼for (int i = 0; i < 5;i++){System.out.println("main AThread:" + i);}// 可能存在一個(gè)gc線程} }// 因?yàn)槎鄠€(gè)線程輪流使用CPU,先占用先使用,所以執(zhí)行結(jié)果不確定2.實(shí)現(xiàn)Runnable接口
/*** MyRun不是線程,但具備在線程中執(zhí)行的能力*/ public class MyRun implements Runnable{@Overridepublic void run() {for (int i = 0; i < 5;i++){System.out.println("main AThread:" + i);}} }// 測(cè)試類: public class Test01 {public static void main(String[] args) {// 1> 創(chuàng)建MyRun對(duì)象MyRun myRun = new MyRun();// 2> 創(chuàng)建線程,把myRun 放入線程中執(zhí)行Thread t = new Thread(myRun);t.start();// main threadfor (int i = 0; i < 5;i++){System.out.println("myRun:" + i);}} }3.兩種實(shí)現(xiàn)方式的區(qū)別
自定義線程的方式有2種,分別是繼承Thread方式和實(shí)現(xiàn)Runnable接口方式 他們的區(qū)別: [1]繼承Thread類后,不能繼承其他類,而實(shí)現(xiàn)Runnable接口還可以繼承其他的類; [2]實(shí)現(xiàn)Runnable接口更方便共享資源.同一份資源,多個(gè)線程并發(fā)訪問(wèn).如果多個(gè)線程要訪問(wèn)共享資源,優(yōu)先考慮Runnable方式;如果線程不訪問(wèn)共享資源,可以考慮用繼承Thread. 多線程訪問(wèn)共享資源的同時(shí),存在一個(gè)嚴(yán)重的問(wèn)題,導(dǎo)致訪問(wèn)共享資源數(shù)據(jù)錯(cuò)誤問(wèn)題。4.多線程的執(zhí)行軌跡
窗口A賣出一張票,還剩3張 窗口C賣出一張票,還剩2張 窗口B賣出一張票,還剩3張 窗口C賣出一張票,還剩0張 窗口A賣出一張票,還剩1張// 假設(shè)窗口B搶占到CPU,執(zhí)行for (int i = 0; i < 5; i++) {if (count > 0) {count--;///System.out.println(...getName() + "買出一張票,還剩" + count + "張");}} // i = 0;i < 5;count > 0條件成立,count-- => count=4; cpu時(shí)間片到,B掛起// A搶占到CPUfor (int i = 0; i < 5; i++) {if (count > 0) {count--;System.out.println(...getName() + "買出一張票,還剩" + count + "張");///}} // i =0;i < 5;count > 0條件成立,count-- => count =3,輸出 窗口 A 買出一張票,還剩 3 張 cpu時(shí)間片到,A掛起// 窗口 B 搶占到 CPU,B 從掛起位置開(kāi)始執(zhí)行for (int i = 0; i < 5; i++) {if (count > 0) {count--;System.out.println(//...getName() + "買出一張票,還剩" + count + "張");}} // 準(zhǔn)備語(yǔ)句:窗口B賣出一張票,還剩3張,還沒(méi)來(lái)得及輸出;cpu時(shí)間片到,B掛起.// C搶占到CPUfor (int i = 0; i < 5; i++) {if (count > 0) {count--;System.out.println(...getName() + "買出一張票,還剩" + count + "張");///}} // i =0;i < 5;count > 0條件成立,count-- => count =2,輸出 窗口 C 買出一張票,還剩 3 張 cpu時(shí)間片到,C掛起// 窗口 B 搶占到 CPU,B 從掛起位置開(kāi)始執(zhí)行for (int i = 0; i < 5; i++) {if (count > 0) {count--;System.out.println(...getName() + "買出一張票,還剩" + count + "張");///}} // 執(zhí)行準(zhǔn)備好的輸出語(yǔ)句"窗口B賣出一張票,還剩3張",繼續(xù)執(zhí)行,執(zhí)行到if(count > 0)掛起// A 搶占到 CPU,A 從掛起的位置開(kāi)始執(zhí)行,for (int i = 0; i < 5; i++) {if (count > 0) {count--;System.out.println(...getName() + "買出一張票,還剩" + count + "張");}} //執(zhí)行到準(zhǔn)備語(yǔ)句 “窗口 A 賣出一張票,還剩 1 張”,還沒(méi)輸出,CPU 時(shí)間片到,A 掛起。// C 搶占 CPU,從掛起位置開(kāi)始執(zhí)行for (int i = 0; i < 5; i++) {if (count > 0) {count--;System.out.println(...getName() + "買出一張票,還剩" + count + "張");}} 輸出 “窗口 C 買出一張票,還剩 0 張”// A 搶占到 CPU,A 從掛起的位置開(kāi)始執(zhí)行,for (int i = 0; i < 5; i++) {if (count > 0) {count--;System.out.println(...getName() + "買出一張票,還剩" + count + "張");}} 執(zhí)行輸出語(yǔ)句 “窗口 A 賣出一張票,還剩 1 張”...總結(jié):
-
線程通過(guò)搶占CPU的方式工作,隨時(shí)肯CPU時(shí)間片到從而被掛起。
-
由于隨時(shí)被掛起或者被切除CPU,導(dǎo)致訪問(wèn)共享資源數(shù)據(jù)錯(cuò)亂。
5.線程的生命周期
新生狀態(tài):用new關(guān)鍵字建立一個(gè)線程后,該對(duì)象就會(huì)處于新生狀態(tài)該線程有資源,通過(guò)調(diào)用start()方法進(jìn)入就緒狀態(tài)就緒狀態(tài):有資源,無(wú)資格:也就是該狀態(tài)下的線程具備了運(yùn)行的條件,但是還沒(méi)分配到CPU.通過(guò)CPU 調(diào)度進(jìn)入運(yùn)行狀態(tài).運(yùn)行狀態(tài):有資源,有資源:該狀態(tài)下的線程具備了運(yùn)行的條件,也分配到了CPU,執(zhí)行自己的run方法 中的代碼,直到完成任務(wù)而死亡或者是因?yàn)榈却迟Y源而造成阻塞.如果在給定的時(shí)間片 內(nèi)沒(méi)有執(zhí)行結(jié)束,就會(huì)被系統(tǒng)給換下來(lái)回到就緒狀態(tài).阻塞狀態(tài):無(wú)資源,讓資格:該狀態(tài)下的線程不具備運(yùn)行的條件,則讓出CPU的資格.造成阻塞狀態(tài)的原因有:[1] 等待I/O資源[2] 等待網(wǎng)絡(luò)資源[3] 線程A join導(dǎo)致其他線程阻塞[4] 線程A sleep導(dǎo)致線程A阻塞當(dāng)阻塞原因消除時(shí)該線程就會(huì)進(jìn)入就緒狀態(tài).死亡狀態(tài):[1]當(dāng)一個(gè)正常運(yùn)行的線程完成了它的全部工作;[2]線程被強(qiáng)制性的終止,如通過(guò)stop()方法終止一個(gè)線程[3]線程拋出未捕獲的異常.6.線程的常用方法
1.線程優(yōu)先級(jí)
public class PriorityThread extends Thread{public PriorityThread() {}public PriorityThread(String name) {super(name);}@Overridepublic void run() {System.out.println(super.getName());} } public class Test01Priority {// 優(yōu)先級(jí)越高,被CPU調(diào)動(dòng)的可能越高,但不一定是優(yōu)先級(jí)越高就一定執(zhí)行// 系統(tǒng)默認(rèn)的三種優(yōu)先級(jí)System.out.println(Thread.MAX_PRIORITY);// 最高System.out.println(Thread.MIN_PRIORITY);// 最低System.out.println(Thread.NORM_PRIORITY);// 默認(rèn)public static void main(String[] args) {PriorityThread ta = new PriorityThread("Ta");ta.setPriority(Thread.MAX_PRIORITY);PriorityThread tb = new PriorityThread("Tb");ta.setPriority(Thread.MIN_PRIORITY);ta.start();tb.start();} }2.線程的強(qiáng)制執(zhí)行 join()
/* t1.join() t1 強(qiáng)制執(zhí)行,導(dǎo)致其他線程(mainThread)阻塞。當(dāng) t1 執(zhí)行完成后,其他線程阻塞原因消除,進(jìn)入就緒態(tài)。 */ public class JoinThread extends Thread{public JoinThread() {}public JoinThread(String name) {super(name);}@Overridepublic void run() {for (int i = 0; i < 10;i++){System.out.println(super.getName() + i);}} } public class Test02Join {public static void main(String[] args) {JoinThread ta = new JoinThread("線程A");ta.start();// main threadfor (int i = 0; i < 10;i++){if (2 == i){// ta強(qiáng)制執(zhí)行try {ta.join();} catch (InterruptedException e) {e.printStackTrace();}}System.out.println("main thread" + i);}} }3.線程休眠 sleep()
/* 線程調(diào)用 sleep(毫秒)方法,當(dāng)前線程進(jìn)入阻塞態(tài),阻塞時(shí)間到后線程進(jìn)入就緒態(tài) */ public class SleepThread extends Thread{public SleepThread() {}public SleepThread(String name) {super(name);}@Overridepublic void run() {System.out.println("開(kāi)始執(zhí)行多線程");try {// 等待10sSystem.out.println("線程即將開(kāi)始休眠...10s");Thread.sleep(10000);} catch (InterruptedException e) {// e.printStackTrace();System.out.println("線程在外界被中斷...");}System.out.println("線程正常結(jié)束");} } public class Test03Sleep {public static void main(String[] args) {SleepThread ta = new SleepThread();ta.start();System.out.println("主線程開(kāi)始休眠");try {Thread.sleep(5000);} catch (InterruptedException e) {e.printStackTrace();}// 試圖打斷ta線程ta.interrupt();} }總結(jié):
總結(jié): [1] 線程休眠導(dǎo)致當(dāng)前線程進(jìn)入阻塞態(tài),休眠時(shí)間結(jié)束后,線程進(jìn)入就緒態(tài),搶占 CPU,搶到后繼續(xù)運(yùn)行。 [2] 線程休眠過(guò)程中可以被中斷 ,所有存在一個(gè)檢查時(shí)異常InterruptedException 異常,外界程序中斷該線程時(shí),休眠時(shí)間提前結(jié)束,進(jìn)入就緒狀態(tài),等待 CPU 調(diào)度執(zhí)行。4.線程禮讓 yield()
public class YieldThread extends Thread{public YieldThread() {}public YieldThread(String name) {super(name);}@Overridepublic void run() {for (int i = 0; i < 10;i++){System.out.println(super.getName() + i);}} }5.線程結(jié)束 interrupt(), stop()(不建議)
/* stop 表示強(qiáng)制停止一個(gè)線程。停止一個(gè)線程的風(fēng)險(xiǎn)較大,現(xiàn)在不建議使用,通過(guò) interrupt 發(fā)中斷信號(hào) 中斷線程,線程會(huì)在何時(shí)的時(shí)間點(diǎn)結(jié)束 interrupt 中止正在運(yùn)行的線程,該線程不會(huì)立即結(jié)束,而是繼續(xù)執(zhí)行。在適當(dāng)?shù)臅r(shí)機(jī)選擇結(jié)合異常處理 機(jī)制,異常處理機(jī)制可以保證線程繼續(xù)執(zhí)行。通過(guò)異常處理讓一個(gè)線程正常結(jié)束。interrupt 方法可以 引導(dǎo)線程正常結(jié)束 */ public class Test03Sleep {public static void main(String[] args) {SleepThread ta = new SleepThread();ta.start();System.out.println("主線程開(kāi)始休眠");try {Thread.sleep(5000);} catch (InterruptedException e) {e.printStackTrace();}// 試圖打斷ta線程ta.interrupt();} }7.線程同步
**原子性操作:**如果希望一系列操作,要么都執(zhí)行,要么都不執(zhí)行。 原子性操作可以認(rèn)為是業(yè)務(wù)上不可分割的單元。
1.同步代碼塊
把原子性操作放到同一個(gè)代碼塊中,就是同步代碼塊,使用關(guān)鍵字synchronized
語(yǔ)法: synchronized(mutex){// 原子性操作 } // mutex稱為同步鎖,也叫互斥鎖 public class MyRun implements Runnable{private int count = 100;@Overridepublic void run() {// 模擬每個(gè)窗口有10個(gè)人買票for (int i = 0;i < 200;i++){synchronized (this){if (count > 0){count--;try {Thread.sleep(200);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(Thread.currentThread().getName()+"已經(jīng)賣出一張,還剩"+count+"張");}}}} } 總結(jié): [1] 如果要實(shí)現(xiàn)原子性操作,必須對(duì)共享資源加鎖(畢加索) [2] 如果線程運(yùn)行時(shí)申請(qǐng)不是該線程加鎖的資源,就會(huì)導(dǎo)致該線程阻塞,進(jìn)入阻塞狀態(tài)同步監(jiān)視器
synchronized(obj){}中的 obj 稱為同步監(jiān)視器
同步代碼塊中同步監(jiān)視器可以是任何對(duì)象,但是推薦使用共享資源作為同步監(jiān)視器,且同步監(jiān)視器不能是基本數(shù)據(jù)類型。
2.同步方法
當(dāng)原子性操作的代碼很長(zhǎng)且可以被重復(fù)調(diào)用時(shí),可以考慮使用同步方法
語(yǔ)法: [修飾符] synchronized 返回值類型 方法名稱(){// 原子性操作 } // 同步方法中無(wú)需指定同步監(jiān)視器,因?yàn)橥椒椒ǖ谋O(jiān)視器是 this,也就是該對(duì)象本身 public class MyRun implements Runnable{private int count = 100;@Overridepublic void run() {// 模擬每個(gè)窗口有10個(gè)人買票for (int i = 0;i < 200;i++){this.saleTicket();}}public synchronized void saleTicket(){if (count > 0){count--;try {Thread.sleep(200);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(Thread.currentThread().getName()+"已經(jīng)賣出一張,還剩"+count+"張");}} }un implements Runnable{
private int count = 100;@Override public void run() {// 模擬每個(gè)窗口有10個(gè)人買票for (int i = 0;i < 200;i++){this.saleTicket();} } public synchronized void saleTicket(){if (count > 0){count--;try {Thread.sleep(200);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(Thread.currentThread().getName()+"已經(jīng)賣出一張,還剩"+count+"張");} }}
總結(jié)
- 上一篇: ppt设置外观样式_PPT中设置视频外观
- 下一篇: 博客在线——Wireshark基本用法