進(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)作,是不需要主線程來完成,因?yàn)檫@樣,會(huì)出現(xiàn)主線程中的代碼執(zhí)行會(huì)停止,會(huì)去運(yùn) 行垃圾回收器代碼,效率較低,所以由單獨(dú)一個(gè)線程來負(fù)責(zé)垃圾回收。
隨機(jī)性的原理:因?yàn)?cpu 的快速切換造成,哪個(gè)線程獲取到了 cpu 的執(zhí)行權(quán),哪個(gè)線程就執(zhí)行。返回當(dāng)前線程的名稱:Thread.currentThread().getName()
線程的名稱是由:Thread-編號(hào)定義的。編號(hào)從 0 開始。
線程要運(yùn)行的代碼都統(tǒng)一存放在了 run 方法中。
線程要運(yùn)行必須要通過類中指定的方法開啟。start 方法。(啟動(dòng)后,就多了一條執(zhí)行路徑)
start 方法:1)、啟動(dòng)了線程;2)、讓 jvm 調(diào)用了 run 方法。
繼承 Thread ,由子類復(fù)寫 run 方法。
步驟:
1,定義類繼承 Thread 類;
2,目的是復(fù)寫 run 方法,將要讓線程運(yùn)行的代碼都存儲(chǔ)到 run 方法中;
3,通過創(chuàng)建 Thread 類的子類對(duì)象,創(chuàng)建線程對(duì)象;
4,調(diào)用線程的 start 方法,開啟線程,并執(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í)行資格,沒有 cpu 的執(zhí)行權(quán);
消亡:stop()
實(shí)現(xiàn)一個(gè)接口 Runnable。
步驟:
1,定義類實(shí)現(xiàn) Runnable 接口。
2,覆蓋接口中的 run 方法(用于封裝線程要運(yùn)行的代碼)。
3,通過 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 方法。開啟線程,并運(yùn)行 Runnable 接口子類中的 run 方法。
Ticket t = new Ticket();
/* 直接創(chuàng)建 Ticket 對(duì)象,并不是創(chuàng)建線程對(duì)象。 因?yàn)閯?chuàng)建對(duì)象只能通過 new Thread 類,或者 new Thread 類的子類才可以。所以最終想要?jiǎng)?chuàng)建線程。既然沒有了 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();
1:通過繼承 Thread 類的方式,可以完成多線程的建立。但是這種方式有一個(gè)局限性,如果一個(gè)類已經(jīng)有了自己的父類,就不可以繼承 Thread 類,因?yàn)?java 單繼承的局限性。
可是該類中的還有部分代碼需要被多個(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 方法,也來自于 Runnable 接口。
實(shí)現(xiàn) Runnable 接口可以避免單繼承的局限性。而且,繼承 Thread,是可以對(duì) Thread 類中的方法,進(jìn)行子類復(fù)寫的。但是不需要做這個(gè)復(fù)寫動(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 切換情況。
通過圖解:發(fā)現(xiàn)一個(gè)線程在執(zhí)行多條語句時(shí),并運(yùn)算同一個(gè)數(shù)據(jù)時(shí),在執(zhí)行過程中,其他線程參與進(jìn)來,并 操作了這個(gè)數(shù)據(jù)。導(dǎo)致到了錯(cuò)誤數(shù)據(jù)的產(chǎn)生。
1,多個(gè)線程在操作共享數(shù)據(jù)。
2,有多條語句對(duì)共享數(shù)據(jù)進(jìn)行運(yùn)算。
原因:這多條語句,在某一個(gè)時(shí)刻被一個(gè)線程執(zhí)行時(shí),還沒有執(zhí)行完,就被其他線程執(zhí)行了。
只要將操作共享數(shù)據(jù)的語句在某一時(shí)段讓一個(gè)線程執(zhí)行完,在執(zhí)行過程中,其他線程不能進(jìn)來執(zhí)行就可以解決 這個(gè)問題。
java 中提供了一個(gè)解決方式:就是同步代碼塊。格式:
synchronized(對(duì)象) { // 任意對(duì)象都可以。這個(gè)對(duì)象就是鎖。需要被同步的代碼; }
聯(lián)系客服