多線程,一個(gè)讓我學(xué)生時(shí)代感覺逼格較高的詞語之一,因?yàn)槁牪欢.?dāng)時(shí)聽java多線程這部分課程時(shí),已忘記我在干嘛,反正沒聽課。只記得有次在機(jī)房上機(jī),一男同學(xué)好像在看電視劇《老公的春天》,還被老師逮住了,哈哈哈。俗話說“躲得過初一,躲不過十五”,欠下的終歸要還的......
線程&多線程
線程的意義,在上篇中就有提到,它作為操作系統(tǒng)調(diào)度的最小單元,多個(gè)線程能一塊執(zhí)行,提升性能,尤其在多處理器中,就不再多說了。
線程的創(chuàng)建與啟動(dòng)
線程的啟動(dòng)基本有三種方式:①繼承Thread,重寫run()方法 ②實(shí)現(xiàn)Runnable接口,重寫run()方法 ③創(chuàng)建FutureTask對象,創(chuàng)建Callable子類對象,重寫call()方法。這篇先介紹下前兩種,因?yàn)樗鼉蛇\(yùn)行完run()完沒有返回值,而第三種是有返回值的:
繼承Thread
public class Test1 extends Thread {
@Override
public void run() {
for(int i = 0; i < 10; i++){
System.out.println("當(dāng)前線程名為:"+Thread.currentThread().getName() + "***哈哈哈***:" + i);
}
}
public static void main(String[] args) {
Test1 thread1 = new Test1();
thread1.start();
}
}
執(zhí)行結(jié)果如下:

這是一個(gè)最簡單的線程使用,只創(chuàng)建了一個(gè)線程對象,還是單線程,再創(chuàng)建一個(gè)thread2,就是多線程了。修改上面的的main函數(shù):
public static void main(String[] args) {
Test1 thread1 = new Test1();
Test1 thread2 = new Test1();
thread1.start();
thread2.start();
}
結(jié)果如下:

啟動(dòng)線程的方式就是執(zhí)行Thread的start()方法,strat()方法是一個(gè)native方法,它將啟動(dòng)一個(gè)新線程,并執(zhí)行run()方法,可以瞅瞅它的實(shí)現(xiàn):
public synchronized void start() {
if (threadStatus != 0)
throw new IllegalThreadStateException();
group.add(this);
boolean started = false;
try {
start0();
started = true;
} finally {
try {
if (!started) {
group.threadStartFailed(this);
}
} catch (Throwable ignore) {
}
}
}
private native void start0();
如果一個(gè)線程對象執(zhí)行run()方法,不執(zhí)行start()方法,結(jié)果是怎樣的?繼續(xù)拿第一個(gè)單線程來試驗(yàn):
public static void main(String[] args) {
Test1 thread1 = new Test1();
thread1.run();
}
結(jié)果和thread.start()一模一樣,因?yàn)門hread也是Runnable的實(shí)現(xiàn)類。

那么如果一個(gè)線程對象多次調(diào)用start()方法和run()方法的情況是怎樣的?來,接著試:
public static void main(String[] args) {
Test1 thread1 = new Test1();
thread1.start();
thread1.start();
}
結(jié)果報(bào)錯(cuò):

在Thread的start()方法處打斷點(diǎn),可以看到thread1對象第一次執(zhí)行start()方法時(shí),threadStatus=0,成功執(zhí)行;當(dāng)thread1第二次執(zhí)行start()方法時(shí),threadStatus=2(這個(gè)狀態(tài)值并不是固定的,但都非零),拋出了異常,說明了同一個(gè)線程對象不能多次執(zhí)行start()方法。


接著咱們看一個(gè)線程對象多次執(zhí)行run()方法:
public static void main(String[] args) {
Test1 thread1 = new Test1();
thread1.run();
thread1.run();
}
執(zhí)行結(jié)果:

可以看到,這時(shí)候并沒有開啟新的線程,main線程直接調(diào)用執(zhí)行了run()方法中代碼。所以,start()會(huì)開啟新的線程并在新線程中執(zhí)行run()方法,而run()方法不會(huì)開啟新線程。
實(shí)現(xiàn)Runnable接口
public class Test2 implements Runnable {
@Override
public void run() {
for(int i =0; i < 10; i++){
System.out.println("當(dāng)前線程名為:"+Thread.currentThread().getName() + "***哈哈哈***:" + i);
}
}
public static void main(String[] args) {
Test2 test2 = new Test2();
Thread thread1 = new Thread(test2);
thread1.start();
}
}
執(zhí)行結(jié)果:

這個(gè)也是單線程,為了啟動(dòng)線程,先創(chuàng)建一個(gè)Thread實(shí)例,再將Test2實(shí)例傳入,在Thread所實(shí)現(xiàn)的run()方法處打斷點(diǎn),可以看見Thread的run()方法就會(huì)調(diào)用target.run()。

小結(jié)
此篇主要介紹了兩種無返回結(jié)果的線程創(chuàng)建方式。兩種方式的區(qū)別如下:①繼承Thread這種不是很靈活,因Java的單一繼承性,繼承了Thread就別再妄想繼承別的類了;②實(shí)現(xiàn)Runnable接口這種方式就巧妙地避開了這個(gè)弊端,更加符合面向?qū)ο蟮木幊谭绞?,也降低了線程對象和線程任務(wù)的耦合性。總之,建議使用第二種實(shí)現(xiàn)Runnable的方式來創(chuàng)建線程。