多線程內(nèi)容比較多悯周,今天寫完了第二篇粒督,后邊應(yīng)該還有三或者四。
1. 同步方法和同步塊禽翼,哪個(gè)是更好的選擇屠橄?
(1)同步方法:即有synchronized關(guān)鍵字修飾的方法。 由于java的每個(gè)對(duì)象都有一個(gè)內(nèi)置鎖闰挡,當(dāng)用此關(guān)鍵字修飾方法時(shí)锐墙, 內(nèi)置鎖會(huì)保護(hù)整個(gè)方法。在調(diào)用該方法前长酗,需要獲得內(nèi)置鎖溪北,否則就處于阻塞狀態(tài)。synchronized關(guān)鍵字也可以修飾靜態(tài)方法夺脾,此時(shí)如果調(diào)用該靜態(tài)方法之拨,將會(huì)鎖住整個(gè)類。
(2)同步塊:即有synchronized關(guān)鍵字修飾的語(yǔ)句塊咧叭。 被該關(guān)鍵字修飾的語(yǔ)句塊會(huì)自動(dòng)被加上內(nèi)置鎖蚀乔,從而實(shí)現(xiàn)同步。
(3)同步是一種高開銷的操作菲茬,因此應(yīng)該盡量減少同步的內(nèi)容吉挣。 通常沒有必要同步整個(gè)方法,使用synchronized代碼塊同步關(guān)鍵代碼即可婉弹。
2. run()和start()方法區(qū)別
(1)run()是實(shí)現(xiàn)Runable接口和繼承Thread必須重寫的睬魂,多線程實(shí)現(xiàn)的主方法,寫的是具體的多線程執(zhí)行的編碼內(nèi)容镀赌。
(2)start()方法是啟動(dòng)執(zhí)行多線程的方法氯哮,表示真正開始運(yùn)行多線程方法。
3. 如何控制某個(gè)方法允許并發(fā)訪問線程的個(gè)數(shù)佩脊?
Semaphore創(chuàng)建時(shí)設(shè)定最多同時(shí)訪問個(gè)數(shù)蛙粘,acquire()申請(qǐng)新請(qǐng)求計(jì)數(shù)+1垫卤,達(dá)到設(shè)定數(shù)值后,內(nèi)部會(huì)執(zhí)行Thread的掛起操作出牧,并把超過指定個(gè)數(shù)的線程放入到隊(duì)列中等待喚醒穴肘。當(dāng)release()后會(huì)喚醒隊(duì)列里的前幾個(gè)線程執(zhí)行。
import java.util.concurrent.Semaphore;
public class SemaphoreTest {
static Semaphore semaphore = new Semaphore(2, true);
public static void main(String[] args) {
for (int i = 0; i < 100; i++) {
new Thread(new Runnable() {
@Override
public void run() {
test();
}
}).start();
}
}
public static void test() {
try {
//這塊可以運(yùn)行時(shí)可以看到實(shí)際時(shí)先打印出來舔痕,說明已經(jīng)進(jìn)入了方法了
System.out.println("waiting"+Thread.currentThread().getName());
//申請(qǐng)一個(gè)新的請(qǐng)求
semaphore.acquire();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"進(jìn)來了");
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"走了");
//釋放一個(gè)請(qǐng)求
semaphore.release();
}
}
4. 在Java中wait和seelp方法的不同评抚;
(1)wait()方法,是屬于Object類伯复,wait()出發(fā)后慨代,線程會(huì)放棄對(duì)象鎖,進(jìn)入等待此對(duì)象的等待鎖定池啸如,只有針對(duì)此對(duì)象調(diào)用notify()方法后本線程才進(jìn)入對(duì)象鎖定池準(zhǔn)備獲取對(duì)象鎖進(jìn)入運(yùn)行狀態(tài)
(2)sleep()方法侍匙,是屬于Thread類的,sleep()方法的執(zhí)行過程中叮雳,線程不會(huì)釋放對(duì)象鎖
5. Thread類中的yield方法有什么作用想暗?
Thread.yield()可以暫停當(dāng)前正在執(zhí)行的線程對(duì)象,讓其他有相同優(yōu)先級(jí)的線程執(zhí)行帘不。它是一個(gè)靜態(tài)方法而且只保證當(dāng)前線程放棄CPU占用而不能保證其它線程一定能占用CPU说莫,執(zhí)行yield()的線程有可能在進(jìn)入到暫停狀態(tài)后馬上又被執(zhí)行。
6. 什么是不可變對(duì)象寞焙,它對(duì)寫并發(fā)應(yīng)用有什么幫助储狭?
對(duì)象一旦被創(chuàng)建它的狀態(tài)(對(duì)象的數(shù)據(jù),也即對(duì)象屬性值)就不能改變捣郊, 例如String辽狈、基本類型的包裝類(Long,Double等)模她、BigInteger和BigDecimal等稻艰。主要是線程安全,在多線程情況下不可改變侈净,天生線程安全尊勿。
7. 談?wù)剋ait/notify關(guān)鍵字的理解
都是繼承自O(shè)bject的方法,wait()方法用來將當(dāng)前線程置入休眠狀態(tài)畜侦,notify()用來通知那些可能等待該對(duì)象的對(duì)象鎖的其他線程元扔。如果有多個(gè)線程等待,則線程規(guī)劃器任意挑選出其中一個(gè)wait()狀態(tài)的線程來發(fā)出通知旋膳,并使它等待獲取該對(duì)象的對(duì)象鎖澎语。
8. 為什么wait, notify 和 notifyAll這些方法不在thread類里面?
簡(jiǎn)單的說,由于wait擅羞,notify和notifyAll都是鎖級(jí)別的操作尸变,所以把他們定義在Object類中,因?yàn)殒i屬于對(duì)象减俏。
那么復(fù)雜的說呢召烂?因?yàn)殒i是在對(duì)象頭中的markWorld的中標(biāo)記的,wait和notify直接理解為get和set方法娃承,實(shí)際上就是對(duì)鎖的操作奏夫,因此理解來說,這個(gè)應(yīng)該放到Object類中历筝,而不是在Thread類中酗昼。
9. 什么導(dǎo)致線程阻塞恬涧?
(1)Thread.sleep方法休眠一段時(shí)間枯跑,線程放棄cpu逗旁,一段時(shí)間后在恢復(fù)運(yùn)行
(2)線程執(zhí)行wait()方法跷叉,進(jìn)入阻塞狀態(tài),直到notify或者notifyAll方法喚醒
(3)等待相關(guān)資源:線程執(zhí)行I/O操作或進(jìn)行遠(yuǎn)程通信時(shí)彪置,會(huì)因?yàn)榈却嚓P(guān)的資源而進(jìn)入阻塞狀態(tài)逞频。例如線程執(zhí)行System.in.read()時(shí),如果用戶沒有輸入惕稻,則一直等待
(4)調(diào)用了yield方法后,同樣會(huì)進(jìn)入阻塞狀態(tài)蝙叛。
(5)suspend() 是讓線程進(jìn)入阻塞狀態(tài)俺祠,沒有resume()是不會(huì)恢復(fù)的。
(6)join()方法借帘,被join的Thread要等待join的Thread執(zhí)行完畢才能繼續(xù)執(zhí)行蜘渣。
10. 講一下java中的同步的方法
(1)當(dāng)多個(gè)線程同時(shí)操作一個(gè)可共享的資源變量時(shí)(如數(shù)據(jù)的增刪改查),將會(huì)導(dǎo)致數(shù)據(jù)不準(zhǔn)確肺然,相互之間產(chǎn)生沖突蔫缸,因此加入同步鎖以避免在該線程沒有完成操作之前,不被其他線程的調(diào)用际起,從而保證了該變量的唯一性和準(zhǔn)確性拾碌。
(2)同步方法有synchronized關(guān)鍵字修飾的方法。由于java的每個(gè)對(duì)象都有一個(gè)內(nèi)置鎖街望,當(dāng)用此關(guān)鍵字修飾方法時(shí)校翔, 內(nèi)置鎖會(huì)保護(hù)整個(gè)方法。在調(diào)用該方法前灾前,需要獲得內(nèi)置鎖防症,否則就處于阻塞狀態(tài)。
(3)synchronized關(guān)鍵字也可以修飾靜態(tài)方法,此時(shí)如果調(diào)用該靜態(tài)方法蔫敲,將會(huì)鎖住整個(gè)類饲嗽。
(4)synchronized代碼塊同步關(guān)鍵代碼
(5)使用特殊域變量(volatile)實(shí)現(xiàn)線程同步,但是不能完全避免同步問題奈嘿,因?yàn)槠洳荒鼙WC原子操作喝噪,它只是在取得該變量值的時(shí)候是從內(nèi)存中讀取的而不是存緩存中讀取。
(6)使用重入鎖實(shí)現(xiàn)線程同步指么,通過lock和unlock來控制方法的執(zhí)行鎖酝惧。
(7)ThreadLocal的方式,由于每個(gè)線程都是獨(dú)立的ThreadLocal伯诬,所以其實(shí)并不存在什么同步晚唇,以及沖突,每個(gè)線程都在自己的空間內(nèi)執(zhí)行盗似。
public class Bank {
//實(shí)際上這個(gè)是每個(gè)線程獨(dú)立的ThreadLocal哩陕,并不共享,因此多個(gè)線程調(diào)用addMoney
//或者subMoney的時(shí)候赫舒,都是在自己的獨(dú)立線程內(nèi)的悍及,并不共享,不存在沖突
private static ThreadLocal<Integer> count = new ThreadLocal<Integer>() {
@Override
protected Integer initialValue() {
return 0;
}
};
// 存錢
public void addMoney(int money) {
count.set(count.get() + money);
System.out.println(System.currentTimeMillis() + "存進(jìn):" + money);
}
//取錢
public void subMoney(int money) {
if (count.get() - money < 0) {
System.out.println("余額不足");
return;
}
count.set(count.get() - money);
System.out.println(+System.currentTimeMillis() + "取出:" + money);
}
//查詢
public void lookMoney() {
System.out.println("賬戶余額:" + count.get());
}
}