開啟線程的三種方式合住?
- 繼承Thread類宅粥,重寫run(),調(diào)用start;
- 實(shí)現(xiàn)Runnable接口偎血,復(fù)寫run(),將Runnable子類對(duì)象傳遞給Thread類對(duì)象华望,調(diào)用start;
- 創(chuàng)建FutureTask對(duì)象哟玷,創(chuàng)建Callable子類對(duì)象狮辽。重寫call(相當(dāng)于run)方法一也,將其傳遞給FutureTask對(duì)象(相當(dāng)于一個(gè)Runnable)。創(chuàng)建Thread類對(duì)象喉脖,將FutureTask對(duì)象傳遞給Thread對(duì)象椰苟。調(diào)用start方法開啟線程。這種方式可以獲得線程執(zhí)行完之后的返回值树叽。
run()和start()方法區(qū)別
run()是一個(gè)普通方法舆蝴,多線程中可以多次調(diào)用,start()開啟一個(gè)線程题诵。jvm內(nèi)部機(jī)制規(guī)定在調(diào)用start()開啟線程時(shí)洁仗,會(huì)自動(dòng)調(diào)用run().
如何控制某個(gè)方法允許并發(fā)訪問線程的個(gè)數(shù)?
創(chuàng)建一個(gè)Semaphore對(duì)象性锭,初始化訪問線程的個(gè)數(shù)赠潦,在線程開始時(shí)通過acquire()請(qǐng)求信號(hào),線程完成時(shí)release()釋放信號(hào)即可控制訪問線程的個(gè)數(shù)草冈。
Java中wait和seelp方法的不同
sleep()屬于Thread類中的她奥,wait()屬于Object。
sleep()睡眠時(shí)怎棱,保持對(duì)象鎖哩俭,而wait()釋放對(duì)象鎖。wait()通過notify或notifyAll或指定時(shí)間來喚醒當(dāng)前線程(需要獲得鎖對(duì)象)拳恋,必須放在Snchronized block中凡资,否則會(huì)拋出IllegalMonitorStateException
wait/notify關(guān)鍵字的理解
wait( ),notify( )谬运,notifyAll( )屬于Object基礎(chǔ)類隙赁,也就是每個(gè)對(duì)象都有wait( ),notify( )吩谦,notifyAll( ) 的功能鸳谜,因?yàn)槊總€(gè)對(duì)象都有鎖,鎖是每個(gè)對(duì)象的基礎(chǔ)式廷,當(dāng)然操作鎖的方法也是最基礎(chǔ)了咐扭。
當(dāng)需要調(diào)用以上的方法的時(shí)候,一定要對(duì)競(jìng)爭(zhēng)資源進(jìn)行加鎖滑废,如果不加鎖的話蝗肪,則會(huì)報(bào) IllegalMonitorStateException 異常
當(dāng)想要調(diào)用wait( )進(jìn)行線程等待時(shí),必須要取得這個(gè)鎖對(duì)象的控制權(quán)(對(duì)象監(jiān)視器)蠕趁,一般是放到synchronized(obj)代碼中薛闪。
在while循環(huán)里而不是if語句下使用wait,這樣俺陋,會(huì)在線程暫突硌樱恢復(fù)后都檢查wait的條件昙篙,并在條件實(shí)際上并未改變的情況下處理喚醒通知
調(diào)用obj.wait( )釋放了obj的鎖,否則其他線程也無法獲得obj的鎖诱咏,也就無法在synchronized(obj){ obj.notify() } 代碼段內(nèi)喚醒A苔可。
notify( )方法只會(huì)通知等待隊(duì)列中的第一個(gè)相關(guān)線程(不會(huì)通知優(yōu)先級(jí)比較高的線程)
notifyAll( )通知所有等待該競(jìng)爭(zhēng)資源的線程(也不會(huì)按照線程的優(yōu)先級(jí)來執(zhí)行)
假設(shè)有三個(gè)線程執(zhí)行了obj.wait( ),那么obj.notifyAll( )則能全部喚醒tread1袋狞,thread2焚辅,thread3,但是要繼續(xù)執(zhí)行obj.wait()的下一條語句苟鸯,必須獲得obj鎖同蜻,因此,tread1早处,thread2湾蔓,thread3只有一個(gè)有機(jī)會(huì)獲得鎖繼續(xù)執(zhí)行,例如tread1陕赃,其余的需要等待thread1釋放obj鎖之后才能繼續(xù)執(zhí)行卵蛉。
當(dāng)調(diào)用obj.notify/notifyAll后,調(diào)用線程依舊持有obj鎖么库,因此,thread1甘有,thread2诉儒,thread3雖被喚醒,但是仍無法獲得obj鎖亏掀。直到調(diào)用線程退出synchronized塊忱反,釋放obj鎖后,thread1滤愕,thread2温算,thread3中的一個(gè)才有機(jī)會(huì)獲得鎖繼續(xù)執(zhí)行。
什么導(dǎo)致線程阻塞间影?
阻塞狀態(tài)的特點(diǎn)是:該線程放棄CPU的使用注竿,暫停運(yùn)行,等待阻塞原因消除以后才能恢復(fù)運(yùn)行魂贬」睿或者被其他線程中斷退出阻塞狀態(tài),同時(shí)會(huì)拋出InterruptedException付燥。
導(dǎo)致阻塞的原因有:
- sleep()進(jìn)入睡眠狀態(tài)宣谈;
- 線程執(zhí)行一段同步代碼,未獲得相關(guān)同步鎖键科,進(jìn)入阻塞狀態(tài)闻丑;
- 線程調(diào)用對(duì)象的wait方法漩怎,等待其他線程執(zhí)行notify()、notifyAll();
- 線程執(zhí)行某些IO操作嗦嗡,因?yàn)榈却嚓P(guān)的資源而進(jìn)入阻塞狀態(tài)勋锤,比如System. in。
線程如何關(guān)閉酸钦?
- 使用stop()方法強(qiáng)行終止線程怪得,不推薦,可能發(fā)生不可預(yù)料的結(jié)果卑硫;
- 使用退出標(biāo)識(shí)徒恋,使線程正常退出;
public class ThreadSafe extends Thread {
public volatile boolean exit = false;
public void run() {
while (!exit){
//do something
}
}
}
定義了一個(gè)退出標(biāo)志exit欢伏,當(dāng)exit為true時(shí)入挣,while循環(huán)退出,exit的默認(rèn)值為false.在定義exit時(shí)硝拧,使用了一個(gè)Java關(guān)鍵字volatile径筏,這個(gè)關(guān)鍵字的目的是使exit同步,也就是說在同一時(shí)刻只能由一個(gè)線程來修改exit的值.
- 使用interrupt方法中斷線程障陶。
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
/*
* 在這里為一個(gè)循環(huán)滋恬,條件是判斷線程的中斷標(biāo)志位是否中斷
*/
while (true&&(!Thread.currentThread().isInterrupted())) {
try {
Log.i("tag","線程運(yùn)行中"+Thread.currentThread().getId());
// 每執(zhí)行一次暫停40毫秒
//當(dāng)sleep方法拋出InterruptedException 中斷狀態(tài)也會(huì)被清掉
Thread.sleep(40);
} catch (InterruptedException e) {
e.printStackTrace();
//如果拋出異常則再次設(shè)置中斷請(qǐng)求
Thread.currentThread().interrupt();
}
}
}
});
thread.start();
//觸發(fā)條件設(shè)置中斷
thread.interrupt();
java中同步的幾種方式
- 同步方法
- 同步代碼塊
- 特殊域變量,volatile:
- 為變量的訪問提供了一種免鎖機(jī)制抱究;
- 告訴jvm該域可能被其他線程更新恢氯;
- 每次使用都要重新計(jì)算,而不是使用寄存器里的值鼓寺;
- 不會(huì)提供任何原子操作勋拟,不能修飾final類型變量。
- 使用局部變量ThreadLocal實(shí)現(xiàn)
使用局部變量實(shí)現(xiàn)線程同步妈候,如果使用ThreadLocal管理變量敢靡,則每一個(gè)使用該變量的線程都獲得該變量的副本,副本之間相互獨(dú)立苦银,這樣每一個(gè)線程都可以隨意修改自己的變量副本啸胧,而不會(huì)對(duì)其他線程產(chǎn)生影響。
注:ThreadLocal與同步機(jī)制
- ThreadLocal與同步機(jī)制都是為了解決多線程中相同變量的訪問沖突問題墓毒。
- 前者采用以”空間換時(shí)間”的方法吓揪,后者采用以”時(shí)間換空間”的方式
public class Bank{
//使用ThreadLocal類管理共享變量account
private static ThreadLocal<Integer> account = new ThreadLocal<Integer>(){
@Override
protected Integer initialValue(){
return 100;
}
};
public void save(int money){
account.set(account.get()+money);
}
public int getAccount(){
return account.get();
}
}
- 使用重入鎖ReentrantLock
class Bank {
private int account = 100;
//需要聲明這個(gè)鎖
private Lock lock = new ReentrantLock();
public int getAccount() {
return account;
}
//這里不再需要synchronized
public void save(int money) {
lock.lock();
try{
account += money;
}finally{
lock.unlock();
}
}
}
- 使用原子變量實(shí)現(xiàn)線程同步。如AtomicInteger所计,需要線程同步的根本原因就是因?yàn)樽兞康牟僮鞑皇窃有缘?/li>
如何保證線程安全柠辞?如何實(shí)現(xiàn)線程同步?
線程同步主胧,同上叭首。
兩個(gè)進(jìn)程同時(shí)要求寫或者讀习勤,能不能實(shí)現(xiàn)?如何防止進(jìn)程的同步焙格?
- 允許多個(gè)讀者同時(shí)執(zhí)行讀操作图毕;
- 不允許讀者、寫者同時(shí)操作眷唉;
- 不允許多個(gè)寫者同時(shí)操作予颤。
java.util.concurrent.locks.ReadWriteLock;
java.util.concurrent.locks.ReentrantReadWriteLock;
控制訪的數(shù)量即可。Java并發(fā)庫的Semaphore可以完成信號(hào)量的控制冬阳,Semaphore可以控制某個(gè)資源可被同時(shí)訪問的數(shù)量蛤虐,通過acquire()獲取一個(gè)許可,如果沒有就等待肝陪,而release()釋放一個(gè)許可驳庭。
線程間操作List
1使用Collections.synchronizedList()構(gòu)建List;2操作list的方法使用同步鎖。
為何會(huì)兩者都用氯窍,因?yàn)镃ollections.synchronizedList()構(gòu)建的list只針對(duì)list的add(obj)饲常、poll(obj)等方法做同步,在多線程中直接使用方法會(huì)同步,但是在操作list時(shí)狼讨,add(obj)贝淤、poll(obj)方法之前不能保證obj是否被其他線程操作過。