1箭阶、實(shí)現(xiàn)多線程的方式
1)繼承Thread類
2)實(shí)現(xiàn)Runnable接口
3)Java5以后可通過實(shí)現(xiàn)Callable接口,該接口中的call方法可以在線程執(zhí)行結(jié)束時(shí)產(chǎn)生一個(gè)返回值,代碼如下:
class? MyTask? implements? Callable<Integer>{
? ? ? private int upperBounds;
????? public MyTask(int upperBounds) {
????????? this.upperBounds = upperBounds;
????? }
@Override
public Integer call() throws Exception {
int sum = 0;
for(int i = 1; i <= upperBounds; i++) {
sum += i;
}
return sum;
}
}
public class Test {? ? ? ? public static void main(String[] args) throws Exception {? ? ? ? ? List> list = new ArrayList<>();? ? ? ? ? ExecutorService service = Executors.newFixedThreadPool(10);? ? ? ? ? for(int i = 0; i < 10; i++) {? ? ? ? ? ? ? list.add(service.submit(new MyTask((int) (Math.random() * 100))));? ? ? ? ? }? ? ? ? ? ? ? ? ? ? int sum = 0;? ? ? ? ? for(Futurefuture : list) {
while(!future.isDone()) ;
sum += future.get();
}
System.out.println(sum);
}
}
2、實(shí)現(xiàn)Runnable接口相比繼承thread類的優(yōu)勢(shì)
1)可以避免Java中單繼承帶來的局限
2)可增強(qiáng)程序的健壯性,代碼能夠被多個(gè)線程共享丹擎,代碼與數(shù)據(jù)是獨(dú)立的
3)適合多個(gè)相同程序代碼的線程取處理同一個(gè)資源的情況
3、線程狀態(tài)轉(zhuǎn)換
1)新建狀態(tài)(new):新創(chuàng)建了一個(gè)線程對(duì)象歇父。
2)就緒狀態(tài)(Runnable):線程對(duì)象 創(chuàng)建后蒂培,其他線程調(diào)用了該對(duì)象的start()方法。該狀態(tài)的線程位于可運(yùn)行線程池中庶骄,變得可運(yùn)行毁渗,等待獲取CPU的使用權(quán)践磅。注意:不能對(duì)已啟動(dòng)的線程再次調(diào)用start()方法单刁,否則會(huì)拋出Java.lang.IllegalThreadStateException異常。
3)運(yùn)行狀態(tài)(Running):就緒狀態(tài)的線程獲取了CPU府适,執(zhí)行程序代碼羔飞。
4)阻塞狀態(tài)(Blocked):阻塞狀態(tài)是線程因?yàn)槟撤N原因放棄CPU使用權(quán),暫時(shí)停止運(yùn)行檐春。直到線程進(jìn)入就緒狀態(tài)逻淌,才有機(jī)會(huì)轉(zhuǎn)到運(yùn)行狀態(tài)。阻塞的情況分為三種:
A疟暖、等待阻塞:運(yùn)行的線程執(zhí)行wait()方法卡儒,JVM會(huì)把該線程放入等待池中田柔。
B、同步阻塞:運(yùn)行的線程在獲取對(duì)象的同步鎖時(shí)骨望,若該同步鎖被別的線程占用硬爆,則JVM會(huì)把該線程放入鎖池中。
C擎鸠、其他阻塞:運(yùn)行的線程執(zhí)行sleep()或join()方法缀磕,或者發(fā)出了I/O請(qǐng)求時(shí),JVM會(huì)把該線程置為阻塞狀態(tài)劣光。當(dāng)sleep狀態(tài)超時(shí)袜蚕,join等待線程終止或超時(shí),線程重新轉(zhuǎn)入就緒狀態(tài)绢涡。
5)死亡狀態(tài)(Dead):線程執(zhí)行完了或者因異常退出了run()方法牲剃,該線程結(jié)束生命周期。
4雄可、造成線程阻塞的幾個(gè)方法
1颠黎、線程睡眠--sleep
如果我們需要讓當(dāng)前正在執(zhí)行的線程暫停一段時(shí)間,并進(jìn)入阻塞狀態(tài)滞项,則可以通過調(diào)用Thread的sleep方法狭归。
注意如下幾個(gè)問題:1)sleep是靜態(tài)方法,最好不要用Thread的實(shí)例對(duì)象去調(diào)用它文判,因?yàn)樗叩氖冀K是當(dāng)前正在運(yùn)行的線程而不是調(diào)用它自己的線程對(duì)象过椎,sleep方法只對(duì)正在運(yùn)行狀態(tài)的線程對(duì)象有效。
publicstaticvoidmain(String[] args) throws InterruptedException {
System.out.println(Thread.currentThread().getName());
MyThread?myThread=newMyThread();
myThread.start();
myThread.sleep(1000);//這里sleep的就是main線程戏仓,而非myThread線程
MyThread.sleep(1000);//也是main線程
Thread.sleep(10);
for(inti=0;i<100;i++){
System.out.println("main"+i);
}
}
2)Java線程調(diào)度是Java多線程的核心疚宇,只有良好的調(diào)度,才能充分發(fā)揮系統(tǒng)的性能赏殃,提高程序的執(zhí)行效率敷待。但是不管程序員怎么編寫調(diào)度,只能最大限度的影響線程執(zhí)行的次序仁热,而不能做到精準(zhǔn)控制榜揖。因?yàn)槭褂胹leep方法之后,線程是進(jìn)入阻塞狀態(tài)的抗蠢,只有當(dāng)睡眠的時(shí)間結(jié)束举哟,才會(huì)重新進(jìn)入到就緒狀態(tài),而就緒狀態(tài)進(jìn)入到運(yùn)行狀態(tài)是由系統(tǒng)控制的迅矛,我們不可能精準(zhǔn)的去干涉它妨猩,所以如果調(diào)用thread.sleep(1000);使得線程睡眠1秒,可能結(jié)果會(huì)大于1秒秽褒。
2壶硅、線程讓步--yield
yield方法和sleep方法有點(diǎn)相似威兜,它也是thread類提供的一個(gè)靜態(tài)方法,它可以讓當(dāng)前正在執(zhí)行的線程暫停庐椒,讓出cpu資源給其他的線程牡属。但是和sleep方法不同的是,它不會(huì)讓線程進(jìn)入到阻塞狀態(tài)扼睬,而是進(jìn)入到就緒狀態(tài)逮栅。yield方法只是讓當(dāng)前線程暫停一下,重新進(jìn)入就緒的線程池中窗宇,讓系統(tǒng)的線程調(diào)度器重新調(diào)度一次措伐,完全可能出現(xiàn)這樣的情況:當(dāng)某個(gè)線程調(diào)用yield方法后,線程調(diào)度器又將其調(diào)度出來重新進(jìn)入到運(yùn)行狀態(tài)執(zhí)行军俊。
①侥加、sleep方法暫停當(dāng)前線程后,會(huì)進(jìn)入阻塞狀態(tài)粪躬,只有當(dāng)睡眠時(shí)間到了担败,才會(huì)轉(zhuǎn)入就緒狀態(tài)。而yield方法調(diào)用后 镰官,是直接進(jìn)入就緒狀態(tài)提前,所以有可能剛進(jìn)入就緒狀態(tài),又被調(diào)度到運(yùn)行狀態(tài)泳唠。
②狈网、sleep方法聲明拋出了InterruptedException,所以調(diào)用sleep方法的時(shí)候要捕獲該異常笨腥,或者顯示聲明拋出該異常拓哺。而yield方法則沒有聲明拋出任務(wù)異常。
③脖母、sleep方法比yield方法有更好的可移植性士鸥,通常不要依靠yield方法來控制并發(fā)線程的執(zhí)行。