一、進(jìn)程和線程
進(jìn)程:進(jìn)程是具有一定獨(dú)立功能的程序關(guān)于某個(gè)數(shù)據(jù)集合上的一次運(yùn)行活動(dòng),進(jìn)程是系統(tǒng)進(jìn)行資源分配和調(diào)度的一個(gè)獨(dú)立單位.
線程:線程是進(jìn)程的一個(gè)實(shí)體,是CPU調(diào)度和分派的基本單位,它是比進(jìn)程更小的能獨(dú)立運(yùn)行的基本單位.線程自己基本上不擁有系統(tǒng)資源,只擁有一點(diǎn)在運(yùn)行中必不可少的資源(如程序計(jì)數(shù)器,一組寄存器和棧),但是它可與同屬一個(gè)進(jìn)程的其他的線程共享進(jìn)程所擁有的全部資源.
特點(diǎn)與區(qū)別:
1、簡而言之,一個(gè)程序至少有一個(gè)進(jìn)程,一個(gè)進(jìn)程至少有一個(gè)線程.
2、線程的劃分尺度小于進(jìn)程,使得多線程程序的并發(fā)性高。
3、另外,進(jìn)程在執(zhí)行過程中擁有獨(dú)立的內(nèi)存單元,而多個(gè)線程共享內(nèi)存,從而極大地提高了程序的運(yùn)行效率。
4、線程在執(zhí)行過程中與進(jìn)程還是有區(qū)別的。每個(gè)獨(dú)立的線程有一個(gè)程序運(yùn)行的入口、順序執(zhí)行序列和程序的出口。但是線程不能夠獨(dú)立執(zhí)行,必須依存在應(yīng)用程序中,由應(yīng)用程序提供多個(gè)線程執(zhí)行控制。
5、從邏輯角度來看,多線程的意義在于一個(gè)應(yīng)用程序中,有多個(gè)執(zhí)行部分可以同時(shí)執(zhí)行。但操作系統(tǒng)并沒有將多個(gè)線程看做多個(gè)獨(dú)立的應(yīng)用,來實(shí)現(xiàn)進(jìn)程的調(diào)度和管理以及資源分配。這就是進(jìn)程和線程的重要區(qū)別。
二、線程的五大基本狀態(tài)及相互轉(zhuǎn)換

1.新建狀態(tài)(New):
當(dāng)用new操作符創(chuàng)建一個(gè)線程時(shí), 例如new Thread(r),線程還沒有開始運(yùn)行,此時(shí)線程處在新建狀態(tài)。 當(dāng)一個(gè)線程處于新生狀態(tài)時(shí),程序還沒有開始運(yùn)行線程中的代碼。
多線程的實(shí)現(xiàn)方法:a、繼承Thread類;b、實(shí)現(xiàn)Runnable接口
2.就緒狀態(tài)(Runnable)
一個(gè)新創(chuàng)建的線程并不自動(dòng)開始運(yùn)行,要執(zhí)行線程,必須調(diào)用線程的start()方法。當(dāng)線程對象調(diào)用start()方法即啟動(dòng)了線程,start()方法創(chuàng)建線程運(yùn)行的系統(tǒng)資源,并調(diào)度線程運(yùn)行run()方法。當(dāng)start()方法返回后,線程就處于就緒狀態(tài)。
處于就緒狀態(tài)的線程并不一定立即運(yùn)行run()方法,線程還必須同其他線程競爭CPU時(shí)間,只有獲得CPU時(shí)間才可以運(yùn)行線程。因?yàn)樵趩蜟PU的計(jì)算機(jī)系統(tǒng)中,不可能同時(shí)運(yùn)行多個(gè)線程,一個(gè)時(shí)刻僅有一個(gè)線程處于運(yùn)行狀態(tài)。因此此時(shí)可能有多個(gè)線程處于就緒狀態(tài)。對多個(gè)處于就緒狀態(tài)的線程是由Java運(yùn)行時(shí)系統(tǒng)的線程調(diào)度程序(thread scheduler)來調(diào)度的。
3.運(yùn)行狀態(tài)(Running)
當(dāng)線程獲得CPU時(shí)間后,它才進(jìn)入運(yùn)行狀態(tài),真正開始執(zhí)行run()方法.
4. 阻塞狀態(tài)(Blocked)
線程運(yùn)行過程中,可能由于各種原因進(jìn)入阻塞狀態(tài):
1>線程通過調(diào)用sleep方法進(jìn)入睡眠狀態(tài);sleep方法必須帶參數(shù),sleep后可以用interrupt喚醒
2>線程調(diào)用一個(gè)在I/O上被阻塞的操作,即該操作在輸入輸出操作完成之前不會(huì)返回到它的調(diào)用者;
3>線程試圖得到一個(gè)鎖,而該鎖正被其他線程持有;
4>線程在等待某個(gè)觸發(fā)條件;
......
所謂阻塞狀態(tài)是正在運(yùn)行的線程沒有運(yùn)行結(jié)束,暫時(shí)讓出CPU,這時(shí)其他處于就緒狀態(tài)的線程就可以獲得CPU時(shí)間,進(jìn)入運(yùn)行狀態(tài)。
join()方法:
當(dāng)A線程執(zhí)行到了B線程的.join()方法時(shí),A就會(huì)等待。等B線程都執(zhí)行完,A才會(huì)執(zhí)行。
join可以用來臨時(shí)加入線程執(zhí)行。
1.線程使用join方法,主線程就停下,等它執(zhí)行完,那么如果該線程凍結(jié)了,主線程就掛了,這也是為什么線程要拋異常的原因
2.當(dāng)兩個(gè)或以上線程開啟了,這個(gè)A線程才使用join方法,那么主線程還是停下,這幾個(gè)個(gè)線程交替進(jìn)行,直到A執(zhí)行完,主線程才復(fù)活
sleep()和wait()方法:
a、(1)首先,調(diào)用sleep()之后,會(huì)引起當(dāng)前執(zhí)行的線程進(jìn)入暫時(shí)中斷狀態(tài),也即睡眠狀態(tài)。
(2)其次,雖然當(dāng)前線程進(jìn)入了睡眠狀態(tài),但是依然持有monitor對象。
(3)在中斷完成之后,自動(dòng)進(jìn)入喚醒狀態(tài)從而繼續(xù)執(zhí)行代碼。
b、(1)首先,調(diào)用了wait()之后會(huì)引起當(dāng)前線程處于等待狀狀態(tài)。
(2)其次,每個(gè)線程必須持有該對象的monitor。如果在當(dāng)前線程中調(diào)用wait()方法之后,該線程就會(huì)釋放monitor的持有對象并讓自己處于等待狀態(tài)。
(3)如果想喚醒一個(gè)正在等待的線程,那么需要開啟一個(gè)線程通過notify()或者notifyAll()方法去通知正在等待的線程獲取monitor對象。如此,該線程即可打破等待的狀態(tài)繼續(xù)執(zhí)行代碼。
5. 死亡狀態(tài)(Dead)
有兩個(gè)原因會(huì)導(dǎo)致線程死亡:
1) run方法正常退出而自然死亡,
2) 一個(gè)未捕獲的異常終止了run方法而使線程猝死。
為了確定線程在當(dāng)前是否存活著(就是要么是可運(yùn)行的,要么是被阻塞了),需要使用isAlive方法。如果是可運(yùn)行或被阻塞,這個(gè)方法返回true; 如果線程仍舊是new狀態(tài)且不是可運(yùn)行的, 或者線程死亡了,則返回false.
三、線程安全和線程同步
線程安全就是多線程訪問時(shí),采用了加鎖機(jī)制,當(dāng)一個(gè)線程訪問該類的某個(gè)數(shù)據(jù)時(shí),進(jìn)行保護(hù),其他線程不能進(jìn)行訪問直到該線程讀取完,其他線程才可使用。不會(huì)出現(xiàn)數(shù)據(jù)不一致或者數(shù)據(jù)污染。
線程不安全就是不提供數(shù)據(jù)訪問保護(hù),有可能出現(xiàn)多個(gè)線程先后更改數(shù)據(jù)造成所得到的數(shù)據(jù)是臟數(shù)據(jù)。
補(bǔ)充:
線程類的一些常用方法:
sleep(): 強(qiáng)迫一個(gè)線程睡眠N毫秒。
isAlive(): 判斷一個(gè)線程是否存活。
join(): 等待線程終止。
activeCount(): 程序中活躍的線程數(shù)。
enumerate(): 枚舉程序中的線程。
currentThread(): 得到當(dāng)前線程。
isDaemon(): 一個(gè)線程是否為守護(hù)線程。
setDaemon(): 設(shè)置一個(gè)線程為守護(hù)線程。(用戶線程和守護(hù)線程的區(qū)別在于,是否等待主線程依賴于主線程結(jié)束而結(jié)束)
setName(): 為線程設(shè)置一個(gè)名稱。
wait(): 強(qiáng)迫一個(gè)線程等待。
notify(): 通知一個(gè)線程繼續(xù)運(yùn)行。
setPriority(): 設(shè)置一個(gè)線程的優(yōu)先級。