教育行業(yè)A股IPO第一股(股票代碼 003032)

全國(guó)咨詢/投訴熱線:400-618-4000

Java培訓(xùn)之java難點(diǎn)解析(十三)-多線程

更新時(shí)間:2016年03月16日16時(shí)01分 來(lái)源:傳智播客Java培訓(xùn)學(xué)院 瀏覽次數(shù):

多線程:★★★★
進(jìn)程:正在進(jìn)行中的程序。其實(shí)進(jìn)程就是一個(gè)應(yīng)用程序運(yùn)行時(shí)的內(nèi)存分配空間。
線程:其實(shí)就是進(jìn)程中一個(gè)程序執(zhí)行控制單元,一條執(zhí)行路徑。進(jìn)程負(fù)責(zé)的是應(yīng)用程序的空間的標(biāo)示。線程負(fù)責(zé)的是應(yīng)用程序的執(zhí)行順序。
 
一個(gè)進(jìn)程至少有一個(gè)線程在運(yùn)行,當(dāng)一個(gè)進(jìn)程中出現(xiàn)多個(gè)線程時(shí),就稱這個(gè)應(yīng)用程序是多線程應(yīng)用程序,每個(gè)線程在棧區(qū)中都有自己的執(zhí)行空間,自己的方法區(qū)、自己的變量。
jvm在啟動(dòng)的時(shí),首先有一個(gè)主線程,負(fù)責(zé)程序的執(zhí)行,調(diào)用的是main函數(shù)。主線程執(zhí)行的代碼都在main方法中。
當(dāng)產(chǎn)生垃圾時(shí),收垃圾的動(dòng)作,是不需要主線程來(lái)完成,因?yàn)檫@樣,會(huì)出現(xiàn)主線程中的代碼執(zhí)行會(huì)停止,會(huì)去運(yùn)行垃圾回收器代碼,效率較低,所以由單獨(dú)一個(gè)線程來(lái)負(fù)責(zé)垃圾回收。 
 
隨機(jī)性的原理:因?yàn)閏pu的快速切換造成,哪個(gè)線程獲取到了cpu的執(zhí)行權(quán),哪個(gè)線程就執(zhí)行。
 
返回當(dāng)前線程的名稱:Thread.currentThread().getName()
線程的名稱是由:Thread-編號(hào)定義的。編號(hào)從0開(kāi)始。
線程要運(yùn)行的代碼都統(tǒng)一存放在了run方法中。
 
線程要運(yùn)行必須要通過(guò)類中指定的方法開(kāi)啟。start方法。(啟動(dòng)后,就多了一條執(zhí)行路徑)
start方法:1)、啟動(dòng)了線程;2)、讓jvm調(diào)用了run方法。
 
創(chuàng)建線程的第一種方式:繼承Thread ,由子類復(fù)寫(xiě)run方法。
步驟:
1,定義類繼承Thread類;
2,目的是復(fù)寫(xiě)run方法,將要讓線程運(yùn)行的代碼都存儲(chǔ)到run方法中;
3,通過(guò)創(chuàng)建Thread類的子類對(duì)象,創(chuàng)建線程對(duì)象;
4,調(diào)用線程的start方法,開(kāi)啟線程,并執(zhí)行run方法。
 
線程狀態(tài):
被創(chuàng)建:start()
運(yùn)行:具備執(zhí)行資格,同時(shí)具備執(zhí)行權(quán);
凍結(jié):sleep(time),wait()—notify()喚醒;線程釋放了執(zhí)行權(quán),同時(shí)釋放執(zhí)行資格;
臨時(shí)阻塞狀態(tài):線程具備cpu的執(zhí)行資格,沒(méi)有cpu的執(zhí)行權(quán);
消亡:stop()

創(chuàng)建線程的第二種方式:實(shí)現(xiàn)一個(gè)接口Runnable。
步驟:
1,定義類實(shí)現(xiàn)Runnable接口。
2,覆蓋接口中的run方法(用于封裝線程要運(yùn)行的代碼)。
3,通過(guò)Thread類創(chuàng)建線程對(duì)象;
4,將實(shí)現(xiàn)了Runnable接口的子類對(duì)象作為實(shí)際參數(shù)傳遞給Thread類中的構(gòu)造函數(shù)。
為什么要傳遞呢?因?yàn)橐尵€程對(duì)象明確要運(yùn)行的run方法所屬的對(duì)象。
5,調(diào)用Thread對(duì)象的start方法。開(kāi)啟線程,并運(yùn)行Runnable接口子類中的run方法。
Ticket t = new Ticket();
/*
直接創(chuàng)建Ticket對(duì)象,并不是創(chuàng)建線程對(duì)象。
因?yàn)閯?chuàng)建對(duì)象只能通過(guò)new Thread類,或者new Thread類的子類才可以。
所以最終想要?jiǎng)?chuàng)建線程。既然沒(méi)有了Thread類的子類,就只能用Thread類。
*/
Thread t1 = new Thread(t); //創(chuàng)建線程。
/*
只要將t作為Thread類的構(gòu)造函數(shù)的實(shí)際參數(shù)傳入即可完成線程對(duì)象和t之間的關(guān)聯(lián)
為什么要將t傳給Thread類的構(gòu)造函數(shù)呢?其實(shí)就是為了明確線程要運(yùn)行的代碼run方法。
*/
t1.start();

為什么要有Runnable接口的出現(xiàn)?
1:通過(guò)繼承Thread類的方式,可以完成多線程的建立。但是這種方式有一個(gè)局限性,如果一個(gè)類已經(jīng)有了自己的父類,就不可以繼承Thread類,因?yàn)閖ava單繼承的局限性。
可是該類中的還有部分代碼需要被多個(gè)線程同時(shí)執(zhí)行。這時(shí)怎么辦呢?
只有對(duì)該類進(jìn)行額外的功能擴(kuò)展,java就提供了一個(gè)接口Runnable。這個(gè)接口中定義了run方法,其實(shí)run方法的定義就是為了存儲(chǔ)多線程要運(yùn)行的代碼。
所以,通常創(chuàng)建線程都用第二種方式。
因?yàn)閷?shí)現(xiàn)Runnable接口可以避免單繼承的局限性。
 
2:其實(shí)是將不同類中需要被多線程執(zhí)行的代碼進(jìn)行抽取。將多線程要運(yùn)行的代碼的位置單獨(dú)定義到接口中。為其他類進(jìn)行功能擴(kuò)展提供了前提。
所以Thread類在描述線程時(shí),內(nèi)部定義的run方法,也來(lái)自于Runnable接口。
 
實(shí)現(xiàn)Runnable接口可以避免單繼承的局限性。而且,繼承Thread,是可以對(duì)Thread類中的方法,進(jìn)行子類復(fù)寫(xiě)的。但是不需要做這個(gè)復(fù)寫(xiě)動(dòng)作的話,只為定義線程代碼存放位置,實(shí)現(xiàn)Runnable接口更方便一些。所以Runnable接口將線程要執(zhí)行的任務(wù)封裝成了對(duì)象。
-------------------------------------------------------
//面試
new Thread(new Runnable(){  //匿名
public void run(){
System.out.println("runnable run");
}
}
{
public void run(){
System.out.println("subthread run");
}
}.start();  //結(jié)果:subthread run
---------------------------------------------------------
Try {
Thread.sleep(10);
}catch(InterruptedException e){}// 當(dāng)刻意讓線程稍微停一下,模擬cpu 切換情況。
 
多線程安全問(wèn)題的原因:
通過(guò)圖解:發(fā)現(xiàn)一個(gè)線程在執(zhí)行多條語(yǔ)句時(shí),并運(yùn)算同一個(gè)數(shù)據(jù)時(shí),在執(zhí)行過(guò)程中,其他線程參與進(jìn)來(lái),并操作了這個(gè)數(shù)據(jù)。導(dǎo)致到了錯(cuò)誤數(shù)據(jù)的產(chǎn)生。
 
涉及到兩個(gè)因素:
1,多個(gè)線程在操作共享數(shù)據(jù)。
2,有多條語(yǔ)句對(duì)共享數(shù)據(jù)進(jìn)行運(yùn)算。
原因:這多條語(yǔ)句,在某一個(gè)時(shí)刻被一個(gè)線程執(zhí)行時(shí),還沒(méi)有執(zhí)行完,就被其他線程執(zhí)行了。
 
解決安全問(wèn)題的原理:
只要將操作共享數(shù)據(jù)的語(yǔ)句在某一時(shí)段讓一個(gè)線程執(zhí)行完,在執(zhí)行過(guò)程中,其他線程不能進(jìn)來(lái)執(zhí)行就可以解決這個(gè)問(wèn)題。
 
如何進(jìn)行多句操作共享數(shù)據(jù)代碼的封裝呢?
java中提供了一個(gè)解決方式:就是同步代碼塊。
格式:
synchronized(對(duì)象) {  // 任意對(duì)象都可以。這個(gè)對(duì)象就是鎖。
需要被同步的代碼;
}

本文版權(quán)歸傳智播客Java培訓(xùn)學(xué)院所有,歡迎轉(zhuǎn)載,轉(zhuǎn)載請(qǐng)注明作者出處。謝謝!
作者:傳智播客Java培訓(xùn)學(xué)院
首發(fā):http://metathetuscanyresort.com/javaee 
0 分享到:
和我們?cè)诰€交談!