一渴析、多線(xiàn)程
在有多任務(wù)執(zhí)行的需求時(shí)晚伙,如果沒(méi)有任務(wù)并行,不需要執(zhí)行多線(xiàn)程俭茧。
1.優(yōu)點(diǎn)
- 資源利用率更好
- 效率更高
2.缺點(diǎn)
- 程序設(shè)計(jì)復(fù)雜咆疗。
- 需要考慮可能會(huì)出現(xiàn)的數(shù)據(jù)不安全的問(wèn)題。
3.線(xiàn)程和進(jìn)程
3.1進(jìn)程
執(zhí)行中的程序叫做進(jìn)程(process)母债,是一個(gè)動(dòng)態(tài)的概念午磁。
- 每個(gè)進(jìn)程由cpu、data毡们、code組成迅皇,且每個(gè)進(jìn)程都是獨(dú)立的,即便一個(gè)程序產(chǎn)生好幾個(gè)進(jìn)程
- 在執(zhí)行多任務(wù)時(shí)衙熔,操作系統(tǒng)會(huì)將CPU動(dòng)態(tài)的分配給多個(gè)進(jìn)程登颓,每個(gè)進(jìn)程獨(dú)立運(yùn)行
- 進(jìn)程是程序的一次動(dòng)態(tài)執(zhí)行過(guò)程,會(huì)獨(dú)占特定的地址空間
3.2線(xiàn)程
線(xiàn)程(Thread)是操作系統(tǒng)能夠進(jìn)行運(yùn)算調(diào)度的最小單位青责。他被包含在進(jìn)程之中挺据,是進(jìn)程中的實(shí)際運(yùn)作單位取具。一條線(xiàn)程指的是進(jìn)程中單一順序的控制流,一個(gè)進(jìn)程中可以并發(fā)多個(gè)線(xiàn)程扁耐,每條線(xiàn)程并行執(zhí)行多個(gè)線(xiàn)程暇检,每條線(xiàn)程并行執(zhí)行不同的任務(wù)。
3.3線(xiàn)程與進(jìn)程的區(qū)別
區(qū)別 | 進(jìn)程 | 線(xiàn)程 |
---|---|---|
根本區(qū)別 | 作為資源分配的單位 | 調(diào)度和執(zhí)行的單位 |
開(kāi)銷(xiāo) | 每個(gè)進(jìn)程都有獨(dú)立的代碼和數(shù)據(jù)空間(進(jìn)程上下文)婉称,進(jìn)程間的切換會(huì)有較大的開(kāi)銷(xiāo) | 線(xiàn)程可以看做輕量級(jí)的進(jìn)程块仆,同一類(lèi)線(xiàn)程共享代碼和數(shù)據(jù)空間,每個(gè)線(xiàn)程有獨(dú)立運(yùn)行棧和程序計(jì)數(shù)器(PC)王暗,線(xiàn)程切換的開(kāi)銷(xiāo)小 |
所處環(huán)境 | 在操作系統(tǒng)中能同時(shí)運(yùn)行多個(gè)任務(wù)(程序) | 在同一應(yīng)用程序中有多個(gè)順序流同時(shí)執(zhí)行 |
分配內(nèi)存 | 系統(tǒng)在運(yùn)行的時(shí)候會(huì)為每個(gè)進(jìn)程分配不同的內(nèi)存區(qū)域 | 線(xiàn)程間共享進(jìn)程的所有資源悔据,每個(gè)線(xiàn)程只有有自己的堆棧和局部變量。線(xiàn)程由CPU獨(dú)立調(diào)度執(zhí)行俗壹,在多CPU環(huán)境下就允許多個(gè)線(xiàn)程同時(shí)運(yùn)行 |
包含關(guān)系 | 沒(méi)有線(xiàn)程的進(jìn)程可以看作單線(xiàn)程科汗,如果一個(gè)進(jìn)程擁有多個(gè)線(xiàn)程,則執(zhí)行過(guò)程不是一條線(xiàn)的绷雏,而是多條線(xiàn)(線(xiàn)程)共同完成的 | 線(xiàn)程是進(jìn)程的一部分头滔,所以線(xiàn)程有的時(shí)候會(huì)被稱(chēng)為是輕量級(jí)進(jìn)程或輕權(quán)進(jìn)程 |
4.并發(fā)和并行
并行一定是并發(fā),并發(fā)不一定是并行(也可能是串行)
并發(fā):一個(gè)時(shí)間段內(nèi)涎显,程序擁有處理多任務(wù)的能力
并行:一個(gè)時(shí)間段內(nèi)坤检,程序擁有同時(shí)處理多任務(wù)的能力
5.多線(xiàn)程的目標(biāo)
- 創(chuàng)建與開(kāi)啟
- 線(xiàn)程安全
- 線(xiàn)程狀態(tài)
- 線(xiàn)程通信
二、創(chuàng)建線(xiàn)程
1.繼承Thread
public class ThreadDemo extends Thread{
@Override
public void run() {
for (int i = 0;i < 10;i++){
System.out.println("-----"+i);
}
}
public static void main(String[] args) {
ThreadDemo td = new ThreadDemo();
td.start();
for (int i = 0;i < 10;i++){
System.out.println("+++++"+i);
}
}
}
2.實(shí)現(xiàn)Runnable接口
通過(guò)實(shí)現(xiàn)Runnable接口重寫(xiě)run()方法期吓,方法內(nèi)定義線(xiàn)程體早歇,調(diào)用start()方法
public class ThreadDemo02 implements Runnable{
@Override
public void run() {
for (int i = 0;i < 10;i++){
System.out.println("-----"+i);
}
}
public static void main(String[] args) {
Thread t = new Thread(new ThreadDemo02());
t.start();
for (int i = 0;i < 10;i++){
System.out.println("+++++"+i);
}
}
}
注意:run()方法中既不能拋出異常,也不能有返回值讨勤。
3.實(shí)現(xiàn)Callable接口(僅作了解)
重寫(xiě)call方法箭跳,方法中定義線(xiàn)程體,且有返回值
三悬襟、線(xiàn)程狀態(tài)
1.線(xiàn)程的五種狀態(tài)
- 新生狀態(tài):通過(guò)new新建線(xiàn)程對(duì)象衅码,此時(shí)線(xiàn)程處于新生狀態(tài)
- 就緒狀態(tài):調(diào)用start()方法后拯刁,沒(méi)有立即執(zhí)行脊岳,而是進(jìn)入就緒狀態(tài),等待CPU的調(diào)度
- 運(yùn)行狀態(tài):當(dāng)CPU調(diào)度某一個(gè)線(xiàn)程時(shí)垛玻,這個(gè)線(xiàn)程獲取線(xiàn)程體割捅,執(zhí)行線(xiàn)程體代碼
- 阻塞狀態(tài):線(xiàn)程無(wú)法正常運(yùn)行,如執(zhí)行了 sleep(睡眠)方法帚桩,或等待 I/O 設(shè)備等資源亿驾,將讓出 CPU 并暫時(shí)停止自己 運(yùn)行,進(jìn)入阻塞狀態(tài)
- 終止?fàn)顟B(tài):死亡狀態(tài)是線(xiàn)程生命周期中的最后一個(gè)階段账嚎。線(xiàn)程死亡的原因有三個(gè)莫瞬,一個(gè)是正常運(yùn)行的線(xiàn)程完成了全部工作儡蔓;另一個(gè)是線(xiàn)程被強(qiáng)制性地終止,此方法不推薦使用疼邀;第三是線(xiàn)程拋出未捕獲的異常喂江。
Thread.State getState()
返回此線(xiàn)程的狀態(tài)
Thread.State
枚舉類(lèi) 標(biāo)識(shí)線(xiàn)程現(xiàn)有狀態(tài)
注意:
? 一個(gè)線(xiàn)程如果一旦進(jìn)入終止?fàn)顟B(tài)無(wú)法恢復(fù),new之后也是一個(gè)新的線(xiàn)程旁振。
? 如果一個(gè)線(xiàn)程一旦進(jìn)入到阻塞狀態(tài)获询,阻塞解除會(huì)直接恢復(fù)到就緒狀態(tài)不會(huì)直接恢復(fù)到運(yùn)行狀態(tài)。
2.如何進(jìn)入就緒狀態(tài)
- start()方法
- 阻塞解除
- yeild()禮讓線(xiàn)程
- 線(xiàn)程切換
3.如何進(jìn)入阻塞狀態(tài)
- sleep()
- join狀態(tài)
- wait()等待
4.如何進(jìn)入終止?fàn)顟B(tài)
- stop()方法拐袜,不推薦
- destory()方法吉嚣,不推薦
- 通過(guò)添加標(biāo)識(shí)判斷
四、暫停線(xiàn)程執(zhí)行
1.休眠線(xiàn)程(sleep)
sleep(long millis)
導(dǎo)致當(dāng)前正在執(zhí)行的線(xiàn)程休眠(暫時(shí)停止執(zhí)行)指定的毫秒數(shù)蹬铺,具體取決于系統(tǒng)計(jì)時(shí)器和調(diào)度程序的精度和準(zhǔn)確性尝哆。
特點(diǎn):不會(huì)釋放鎖,Sleep 時(shí)別的線(xiàn)程也不可以訪問(wèn)鎖定對(duì)象
作用:
- 模擬網(wǎng)絡(luò)延遲
- 顯現(xiàn)方法問(wèn)題的可能性
2.禮讓線(xiàn)程(yield)
static void yield()
當(dāng)前正在執(zhí)行的線(xiàn)程暫停一次甜攀,允許其他線(xiàn)程 執(zhí)行,不阻塞较解,線(xiàn)程進(jìn)入 就緒狀態(tài),如果沒(méi)有其他等待的線(xiàn)程,這個(gè) 時(shí)候當(dāng)前線(xiàn)程就會(huì)馬上恢復(fù)執(zhí)行赴邻。
特點(diǎn): yield()方法為靜態(tài)方法印衔,讓出 CPU 的使用權(quán),從運(yùn)行態(tài)直接進(jìn)入就緒態(tài)姥敛。讓 CPU重新挑選哪一個(gè)線(xiàn)程進(jìn)入運(yùn)行狀態(tài)奸焙。
3.插隊(duì)線(xiàn)程(join)
final void join()
調(diào)用該方法的線(xiàn)程強(qiáng)制執(zhí)行,其它線(xiàn)程處于阻 塞狀態(tài)彤敛,該線(xiàn)程執(zhí)行完畢后与帆,其它線(xiàn)程再執(zhí)行。
特點(diǎn):當(dāng)某個(gè)線(xiàn)程等待另一個(gè)線(xiàn)程執(zhí)行結(jié)束后墨榄,才繼續(xù)執(zhí)行時(shí)玄糟,使調(diào)用該方法的線(xiàn)程在此之前執(zhí)行完畢,也就是等待調(diào)用該方法的線(xiàn)程執(zhí)行完畢后再往下繼續(xù)執(zhí)行
五袄秩、線(xiàn)程優(yōu)先級(jí)
線(xiàn)程優(yōu)先級(jí)被分為整數(shù)1~10阵翎,且默認(rèn)的優(yōu)先級(jí)為5.
final int getPriority()
獲取線(xiàn)程的優(yōu)先級(jí)
final void setPriority(int priority)
設(shè)置線(xiàn)程的優(yōu)先級(jí)
Thread.MIN_PRIORITY = 1
Thread.MAX_PRIORITY = 10
Thread.NORM_PRIORITY = 5
注意:
優(yōu)先級(jí)低只是意味著獲得調(diào)度的概率低。并不是絕對(duì)先調(diào)用優(yōu)先級(jí)高后調(diào)用優(yōu)先級(jí)低的線(xiàn)程之剧。
public class ThreadTest {
public static void main(String[] args) {
Thread t1 = new Thread(new ThreadDemo(), "t1");
Thread t2 = new Thread(new ThreadDemo(), "t2");
t1.setPriority(1);
t2.setPriority(10);
t1.start();
t2.start();
}
}
class ThreadDemo extends Thread {
public void run() {
for (int i = 0; i < 20; i++) {
System.out.println(Thread.currentThread().getName() + ": " + i);
// yield();
}
}
}
六郭卫、同步與死鎖
1.線(xiàn)程安全
一個(gè)情況下,我們?cè)跒榱颂岣邎?zhí)行效率背稼,我們可能會(huì)使用多線(xiàn)程同時(shí)執(zhí)行多個(gè)方法贰军,對(duì)同一數(shù)據(jù)進(jìn)行多次調(diào)用和覆蓋,此時(shí)蟹肘,數(shù)據(jù)會(huì)造成不準(zhǔn)確和不安全的問(wèn)題词疼。
public class TestSync {
public static void main(String[] args) {
Account a1 = new Account(100, "高");
Drawing draw1 = new Drawing(80, a1);
Drawing draw2 = new Drawing(80, a1);
draw1.start(); //你取錢(qián)
draw2.start(); //你老婆取錢(qián)
}
}
/**
* 簡(jiǎn)單表示銀行賬戶(hù)俯树,將來(lái)打算多個(gè)線(xiàn)程共用的資源
*/
class Account {
int money;
String aname;
public Account(int money, String aname) {
super();
this.money = money;
this.aname = aname;
}
}
/*** 模擬提款操作 * @author Administrator **/
class Drawing extends Thread {
int drawingNum; //取多少錢(qián)
Account account; //要取錢(qián)的賬戶(hù)
int expenseTotal; //總共取的錢(qián)數(shù)
public Drawing(int drawingNum, Account account) {
super();
this.drawingNum = drawingNum;
this.account = account;
}
@Override
public void run() {
if (account.money - drawingNum < 0) {
return;
}
try {
Thread.sleep(1000); //判斷完后阻 塞。其他線(xiàn)程開(kāi)始運(yùn)行贰盗。
} catch (InterruptedException e) {
e.printStackTrace();
}
account.money -= drawingNum;
expenseTotal += drawingNum;
System.out.println(this.getName() + "-- 賬戶(hù)余額:" + account.money);
System.out.println(this.getName() + "-- 總共取了:" + expenseTotal);
}
}
2.線(xiàn)程同步 synchonized
synchonized:同步鎖|排他鎖
線(xiàn)程同步:即當(dāng)有一個(gè)線(xiàn)程在對(duì)內(nèi)存進(jìn)行操作時(shí)聘萨,其他線(xiàn)程都不可以對(duì)這個(gè)內(nèi)存地址進(jìn)行操作,直到該線(xiàn)程完成操作童太, 其他線(xiàn)程才能對(duì)該內(nèi)存地址進(jìn)行操作米辐,而其他線(xiàn)程又處于等待狀態(tài)。
同步:協(xié)同书释、協(xié)助翘贮、互相配合。
2.1 synchornized 方法
public synchronized void accessVal(int newVal){
}
synchronized 方法控制對(duì)類(lèi)成員變量的訪問(wèn):每個(gè)對(duì)象對(duì)應(yīng)一把鎖爆惧,每個(gè) synchronized 方法都必須獲得調(diào)用該 方法的對(duì)象的鎖方能執(zhí)行狸页,否則所屬線(xiàn)程阻塞,方法一 旦執(zhí)行扯再,就獨(dú)占該鎖芍耘,直到從該方法返回時(shí)才將鎖釋放,此后被阻塞的線(xiàn)程方能獲得 該鎖熄阻,重新進(jìn)入可執(zhí)行狀態(tài)斋竞。
synchronized 方法的缺陷:若將一個(gè)大的方法聲明為synchronized 將會(huì)大大影響效率,典型地秃殉,若將線(xiàn)程類(lèi)的方法 run() 聲明為 synchronized 坝初,由于在線(xiàn)程的整個(gè)生命期內(nèi)它一直在運(yùn)行,因此將導(dǎo)致它對(duì)本類(lèi)任何synchronized 方法的調(diào)用都永遠(yuǎn)不會(huì)成功钾军。
2.2 synchoinized 塊
在代碼塊前加上 synchronized 關(guān)鍵字鳄袍,并指定加鎖的對(duì)象
synchronized(syncObject){
//允許訪問(wèn)控制的代碼
}
public class MyClass {
public static synchronized void log1(String msg1, String msg2){
log.writeln(msg1);
log.writeln(msg2);
}
public static void log2(String msg1, String msg2){
synchronized(MyClass.class){
log.writeln(msg1);
log.writeln(msg2);
}
}
}
注意:
- 鎖中內(nèi)容,要鎖不變的東西吏恭,自定義的引用數(shù)據(jù)類(lèi)型的對(duì)象地址拗小,肯定不變
- 代碼范圍太大,容易鎖不住內(nèi)容
雙重檢查(double check)提高效率樱哼,縮小范圍
3.鎖
3.1 鎖類(lèi)
相當(dāng)于鎖住了類(lèi)的所有對(duì)象哀九,有些對(duì)象不需要鎖時(shí),可以選擇使用this
3.2 鎖this
相當(dāng)于鎖住這個(gè)對(duì)象的所有資源唇礁,當(dāng)只想鎖中其中的一個(gè)資源的時(shí)候勾栗,可以只鎖資源
3.3 鎖資源
自定義的引用數(shù)據(jù)類(lèi)型
七惨篱、線(xiàn)程通信
使用Object類(lèi)中的wait()盏筐、notify()|notifyAll()實(shí)現(xiàn)線(xiàn)程通信
-
wait()
等待,當(dāng)一個(gè)線(xiàn)程wait()方法的時(shí)候進(jìn)入到某一個(gè)對(duì)象的等待池中進(jìn)行等待砸讳,等待喚醒并掛起 -
notify()
喚醒琢融,喚醒該對(duì)象等待池中正在等待的線(xiàn)程對(duì)象 -
wait()
與notify()
界牡、notifyAll()
方法必須使用同步環(huán)境下,用來(lái)作為多線(xiàn)程之間數(shù)據(jù)協(xié)調(diào)問(wèn)題,要使用在同步環(huán)境下控制線(xiàn)程安全漾抬,否則會(huì)拋出異常