一窒百、線程與進(jìn)程的關(guān)系
關(guān)于進(jìn)程與線程撮胧,百度百科上是這樣描述的:
進(jìn)程(Process)?是計算機(jī)中的程序關(guān)于某數(shù)據(jù)集合上的一次運行活動是钥,是系統(tǒng)進(jìn)行資源分配和調(diào)度的基本單位僻肖,是操作系統(tǒng)結(jié)構(gòu)的基礎(chǔ)肖爵。 在當(dāng)代面向線程設(shè)計的計算機(jī)結(jié)構(gòu)中,進(jìn)程是線程的容器臀脏。程序是指令劝堪、數(shù)據(jù)及其組織形式的描述冀自,進(jìn)程是程序的實體。是計算機(jī)中的程序關(guān)于某數(shù)據(jù)集合上的一次運行活動秒啦,是系統(tǒng)進(jìn)行資源分配和調(diào)度的基本單位熬粗,是操作系統(tǒng)結(jié)構(gòu)的基礎(chǔ)。程序是指令余境、數(shù)據(jù)及其組織形式的描述驻呐,進(jìn)程是程序的實體插掂。
線程(thread)?是操作系統(tǒng)能夠進(jìn)行運算調(diào)度的最小單位贝咙。它被包含在進(jìn)程之中逆粹,是進(jìn)程中的實際運作單位衙荐。一條線程指的是進(jìn)程中一個單一順序的控制流,一個進(jìn)程中可以并發(fā)多個線程蛉腌,每條線程并行執(zhí)行不同的任務(wù)偿荷。
簡單的總結(jié)一下檐春,就變成了下面的結(jié)果:
進(jìn)程(Process): 程序運行資源分配的最小單位顽聂,進(jìn)程內(nèi)部有多個線程肥惭,會共享這個進(jìn)程的資源。
線程(thread)?: CPU調(diào)度的最小單位紊搪,必須依賴進(jìn)程而存在蜜葱。
也就是說?線程與進(jìn)程的關(guān)系,就像玄幻小說中蟲族的母體和子體一樣嗦明,子體離開了母體便不能存活笼沥。線程亦是如此蚪燕,必須依賴于進(jìn)程而存在娶牌。
二、Java線程的啟動方式
關(guān)于Java的線程馆纳,我們首當(dāng)其沖的會想到j(luò)ava.lang.Thread類诗良,這是一種最簡單的創(chuàng)建線程的方式,我們只需要通過繼承Thread類就可以實現(xiàn):
/** *@authorcai * 通過繼承Thread類的方式 */privatestaticclassUserThreadextendsThread{/**
? ? * 重寫? Thread 類的? run() 方法
? ? */@Overridepublicvoidrun(){? ? ? ? System.out.println("this is a thread for extends Thread");? ? }}
這里鲁驶,我們重寫了Thread類中的run()方法鉴裹,并打印一條語句來表示線程的啟動方式,現(xiàn)在我們來測試一下:
publicstaticvoidmain(String[] args){// 繼承Thread類的方式Thread thread =newUserThread();? ? thread.start();}
控制臺的打印結(jié)果:
thisisa threadforextendsThread
從打印結(jié)果可以看出钥弯,我們的線程正常的啟動径荔,中間沒有出現(xiàn)意外。除了這種方式之外脆霎,JDK內(nèi)部又給我們提供了一個接口類:java.lang.Runnable总处,同樣,我們也可以通過實現(xiàn)該接口睛蛛,重寫run()方法來啟動一個新的線程:
/** *@authorcai * 通過實現(xiàn)Runnable接口的方式 */privatestaticclassUserRunnableimplementsRunnable{/**
? ? * 重寫? Runnable 類的? run() 方法
? ? */@Overridepublicvoidrun(){? ? ? ? System.out.println("this is a thread for implements Runnable");? ? }}
這里我們同樣打印一句話來表示此線程的啟動方式鹦马,現(xiàn)在來測試一下:
publicstaticvoidmain(String[] args)throws{// 實現(xiàn)Runnable接口的方式// 這里注意:所有線程的啟動胧谈,都必須通過調(diào)用Thread類中start()方法來實現(xiàn)Runnable runnable =newUserRunnable();newThread(runnable).start();}
相信上面的代碼,小伙伴們都能看懂(多注意一下第二行的注釋荸频,這是重點)菱肖,現(xiàn)在來看看控制臺的打印結(jié)果:
thisisa threadforimplementsRunnable
同理,這里的線程也正常的啟動了旭从。但看到這里稳强,小伙伴們可能就會產(chǎn)生疑問,為什么JDK要多此一舉和悦,提供了一種Thread類后键袱,為什么還要提供Runnable接口類呢?
在這里給有這個疑惑的小伙伴們答疑下:
因為Java是單繼承摹闽,只能繼承一個類蹄咖,不能繼承多個類。而在我們某些實際的業(yè)務(wù)中付鹿,我們可能需要繼承一個類來處理邏輯業(yè)務(wù)澜汤,那么就不能再繼承Thread類來處理線程相關(guān)的操作了。所以JDK又為我們提供了一個實現(xiàn)Runnable接口的方式舵匾,而且在Java中俊抵,一個類是可以實現(xiàn)多個接口的,這樣我們在使用第二種方式處理線程就不會有顧忌了坐梯。(這里比較有意思的是:Thread類實現(xiàn)了Runnable接口徽诲,有興趣的小伙伴可以去看一下源碼。)
那么除了上面的兩種方式之外吵血,Java是否提供了第三種方式呢谎替?答案是肯定的,從JDK1.5開始蹋辅,JDK為我們提供了一個新的接口類:java.util.concurrent.Callable钱贯,我們可以通過實現(xiàn)這個接口來啟動一個新得線程,而這種方式與實現(xiàn)Runnable接口來啟動線程所不同的是侦另,它會帶有一個返回值秩命,我們來看一下代碼:
/** *@authorcai * 通過實現(xiàn)Callable接口的方式 * 帶返回值 */privatestaticclassUserCallableimplementsCallable{/**? ? * 重寫? Callable 類的? run() 方法? ? *@return*@throwsException? ? */@OverridepublicStringcall()throwsException{return"this is a thread for implements Callable(return String).";? ? }}
測試一下:
<html>
<head></head>
<body>
? publicstaticvoidmain(String[] args)throwsExecutionException, InterruptedException{// 實現(xiàn)Callable接口的方式 帶返回值UserCallable callable =newUserCallable(); FutureTask future =newFutureTask(callable);newThread(future).start(); System.out.println(future.get());}
</body>
</html>
我們這里將返回值打印一下:
thisisa threadforimplementsCallable(returnString).
可以看出,我們的線程正常的啟動褒傅,沒有問題弃锐。
那么看了以上三種Java線程的啟動方式,相信肯定有很多小伙伴會好奇殿托,如果我要中止一個線程霹菊,我需要怎么做呢?讓我們來一起看看吧碌尔。
三浇辜、Java線程的中止
怎樣讓一個正在運行的線程安全的停止工作呢券敌?這里有兩種方法:
1、線程自然的終止:程序正常的執(zhí)行完或者拋出未處理的異常柳洋。
程序正常的執(zhí)行完就不必再說待诅,以上的代碼都屬于此類,我們來看一看拋出未處理異常的情況熊镣,這里我們將上述實現(xiàn)Runnable接口來啟動線程的代碼修改一下:
/** *@authorcai * 通過實現(xiàn)Runnable接口的方式 */privatestaticclassUserRunnableimplementsRunnable{/**
? ? * 重寫? Runnable 類的? run() 方法
? ? */@Overridepublicvoidrun(){// 重點加了這樣的一行代碼inti =10/0;? ? ? ? System.out.println("this is a thread for implements Runnable");? ? }}
這里我們加了一行代碼卑雁,小伙伴們應(yīng)該都可以看懂,這行代碼是必定會拋出異常的绪囱,但我們又沒有對異常進(jìn)行處理测蹲,現(xiàn)在來看一下控制臺的打印結(jié)果:
Exceptioninthread"Thread-0"java.lang.ArithmeticException:/ by zeroat com.cai.thread.create.NewThread$UserRunnable.run(NewThread.java:39)at java.lang.Thread.run(Thread.java:748)
從結(jié)果可以看出,程序運行到了int i = 10 / 0這里就停止了鬼吵,并沒有打印出下一行的結(jié)果扣甲,由此可見,線程到了這里便停止了齿椅,沒有再走下去琉挖。
2、調(diào)用JDK為我們提供的一系列方法
stop()涣脚,resume(),suspend(),interrupt(),isInterrupted(),interrupted()這些方法都是JDK為我們提供的示辈,但是``stop(),resume(),suspend()`已經(jīng)不建議使用了遣蚀,原因如下:
stop()?: 會導(dǎo)致線程不會正常的釋放資源矾麻,“惡意”的中斷當(dāng)前正在運行的線程,不管線程的邏輯是否完整芭梯。
suspend()與resume()?: 極易造成公共的同步對象的獨占险耀,使得其他線程無法訪問公共同步對象,產(chǎn)生死鎖粥帚。(這個例子以后在講線程鎖的時候會解釋胰耗。)
我們先來看一下調(diào)用stop()的例子:
<html>
<head></head>
<body>
? /** *@authorcai * 調(diào)用 stop() 方法的例子 */privatestaticclassUserRunnableimplementsRunnable{@Overridepublicvoidrun(){try{// 讓此線程休眠1秒鐘Thread.sleep(1000); }catch(Exception e){// 異常 捕獲處理}// 此處不會運行,控制臺不會打印// 若實際開發(fā)中芒涡,這里要處理一個很重要的業(yè)務(wù)邏輯,那么這里就會有很大的問題卖漫。// 所以不建議使用System.out.println("代碼到此處不會運行"); }}publicstaticvoidmain(String[] args)throwsInterruptedException{ Runnable runnable =newUserRunnable(); Thread thread =newThread(runnable); thread.start();// 強(qiáng)行中止線程// 從這里可以看出费尽,JDK已經(jīng)不建議使用stop()方法了,添加了@Deprecated注解thread.stop();}
</body>
</html>
我這里將測試代碼也一并貼了上去羊始,可以在代碼的注釋中得到相關(guān)的結(jié)果旱幼。講完這些,我們來看看剩下的三個方法:interrupt(),isInterrupted(),interrupted()
interrupt():調(diào)用此方法會中斷一個線程突委,但并不是強(qiáng)行關(guān)閉這個線程柏卤,只是先給這個線程打個招呼冬三,同事將線程的中斷標(biāo)志位設(shè)置為true,線程是否中斷缘缚,由線程本身決定勾笆。
isInterrupted():判斷當(dāng)前的線程是否處于中斷狀態(tài)(返回true或者false)。
interrupted():靜態(tài)方法桥滨,判斷當(dāng)前的線程是否處于中斷狀態(tài)窝爪,同時將中斷的標(biāo)志位設(shè)置為false。
注意:如果方法中拋出了InterruptedException異常齐媒,那么線程的中斷標(biāo)志位會被復(fù)位成false蒲每,如果確實需要中斷線程的操作,我們需要在catch語句中再次調(diào)用interrupt()方法喻括。
解釋完了邀杏,直接上代碼吧:
<html>
<head></head>
<body>
? /** *@authorcai * 調(diào)用 interrupt(),isInterrupted(),interrupted() 方法的例子 */privatestaticclassUserThreadextendsThread{// 給線程一個名字,創(chuàng)建對象時賦值publicUserThread(String threadName){super(threadName); }@Overridepublicvoidrun(){// 獲得線程的名字String threadName = Thread.currentThread().getName();try{// @2System.out.println(threadName+" flag is "+ isInterrupted());// 休眠一秒鐘 需要捕獲異常 InterruptedException @3Thread.sleep(1000); }catch(InterruptedException e){// 打印一下 isInterrupted() 的狀態(tài) @4System.out.println(threadName+" catch interrput flag is "+ isInterrupted());// 調(diào)用 interrupt() 方法 中斷線程操作 @5interrupt(); }// 打印線程的名字 @6System.out.println(threadName+" interrput flag is "+ interrupted());// @7System.out.println(threadName+" interrput flag is "+ isInterrupted()); }}publicstaticvoidmain(String[] args)throwsInterruptedException{// interrupt(),isInterrupted(),interrupted() 演示Thread thread =newUserThread("caiThread"); thread.start();// @1thread.interrupt();}
</body>
</html>
這里為了方便解釋唬血,我分了步驟:
1淮阐、@1 的位置調(diào)用的interrupt()方法,所以這里的標(biāo)志位是true刁品,所以 @2 的位置打印結(jié)果為true泣特。
2、@3 位置的sleep方法會拋出InterruptedException異常挑随,我這里捕獲了状您,在看之前的理論,拋出此異常兜挨,標(biāo)志位會重置為false膏孟,所以@4 這里的打印結(jié)果為false。
3拌汇、@5 位置再次調(diào)用了interrupt()柒桑,又把標(biāo)志位改為了false,所以 @6 這里打印的結(jié)果為true噪舀,但是這里注意的是魁淳,@6 調(diào)用了interrupted()這個靜態(tài)方法,所以標(biāo)志位又變?yōu)榱薴alse与倡,所以@7 打印的結(jié)果為false界逛。
控制臺打印結(jié)果:
caiThreadcatchflagistruecaiThreadcatchinterrput flagisfalsecaiThread interrput flagistruecaiThread interrput flagisfalse
小伙伴們可以通過對比結(jié)果、代碼和解釋一起看纺座,相信還是很容易明白的息拜。
對線程的了解再多一點點
Java線程總歸下來有五種狀態(tài):
新建、就緒、阻塞少欺、運行喳瓣、死亡
而這里對應(yīng)的方法卻有很多種,具體的關(guān)系赞别,我這里準(zhǔn)備了一張圖:
這張圖上面的各種方法我都會在下次的文章中分享畏陕,這次的分享就到這里,希望大家能夠喜歡氯庆。
最后