并發(fā)面試題:java中有幾種方法可以實(shí)現(xiàn)一個(gè)線程太援?

本文作者:黃海燕闽晦,叩丁狼高級(jí)講師。原創(chuàng)文章提岔,轉(zhuǎn)載請(qǐng)注明出處仙蛉。

1.創(chuàng)建并啟動(dòng)線程的6種方式:

1)繼承Thread類(lèi)創(chuàng)建線程
2)實(shí)現(xiàn)Runnable接口創(chuàng)建線程
3)使用Callable和FutureTask創(chuàng)建線程
4)使用線程池,例如用Executor框架
5)Spring實(shí)現(xiàn)多線程(底層是線程池)
6)定時(shí)器Timer (底層封裝了一個(gè)TimerThread對(duì)象)

1.1 繼承Thread類(lèi)創(chuàng)建線程

1.1.1繼承Thread類(lèi)方式創(chuàng)建線程的實(shí)現(xiàn)步驟:

步驟:
1):定義一個(gè)類(lèi)A繼承于java.lang.Thread類(lèi).
2):在A類(lèi)中覆蓋Thread類(lèi)中的run方法.
3):我們?cè)趓un方法中編寫(xiě)需要執(zhí)行的操作---->run方法里的,線程執(zhí)行體.
4):在main方法(線程)中,創(chuàng)建線程對(duì)象,并啟動(dòng)線程.
????????創(chuàng)建線程類(lèi)對(duì)象: A類(lèi) a = new A類(lèi)();
????????調(diào)用線程對(duì)象的start方法: a.start();//啟動(dòng)一個(gè)線程

注意:千萬(wàn)不要調(diào)用run方法,如果調(diào)用run方法好比是對(duì)象調(diào)用方法,依然還是只有一個(gè)線程,并沒(méi)有開(kāi)啟新的線程.

1.1.2需求:使用兩個(gè)線程實(shí)現(xiàn)邊聽(tīng)歌邊打游戲

實(shí)現(xiàn)代碼:

//音樂(lè)線程
public class MusicThread {
public static void main(String[] args) {
//創(chuàng)建游戲線程對(duì)象
GameThread game = new GameThread();
//啟動(dòng)游戲線程
game.start();
while(true){
System.out.println(Thread.currentThread().getName()+"聽(tīng)音樂(lè)!");
}
}
}
//游戲線程
class GameThread extends Thread{
@Override
public void run() {
while (true) {
System.out.println(Thread.currentThread().getName()+"打游戲!");
}
}
}

注意:有的小伙伴可能覺(jué)得音樂(lè)線程沒(méi)有啟動(dòng),在這里其實(shí)音樂(lè)線程已經(jīng)啟動(dòng)起來(lái)了,而啟動(dòng)音樂(lè)線程的對(duì)象就是我們的JVM,此處main方法其實(shí)啟動(dòng)的時(shí)候會(huì)創(chuàng)建一個(gè)主線程去執(zhí)行main方法,所以我在這里使用主線程作為了我的音樂(lè)線程.

1.2 實(shí)現(xiàn)Runnable接口創(chuàng)建線程

1.2.1實(shí)現(xiàn)Runnable接口方式創(chuàng)建線程的實(shí)現(xiàn)步驟:

1):定義一個(gè)類(lèi)A實(shí)現(xiàn)于java.lang.Runnable接口,注意A類(lèi)不是線程類(lèi).
2):在A類(lèi)中覆蓋Runnable接口中的run方法.
3):我們?cè)趓un方法中編寫(xiě)需要執(zhí)行的操作---->run方法里的,線程執(zhí)行體.
4):在main方法(線程)中,創(chuàng)建線程對(duì)象,并啟動(dòng)線程.
????????創(chuàng)建線程類(lèi)對(duì)象: Thread t = new Thread(new A());
????????調(diào)用線程對(duì)象的start方法: t.start();

1.2.2需求:使用兩個(gè)線程實(shí)現(xiàn)邊聽(tīng)歌邊打游戲

實(shí)現(xiàn)代碼:

//音樂(lè)線程
public class MusicThread {
public static void main(String[] args) {
//創(chuàng)建游戲線程對(duì)象
Thread game = new Thread(new Game());
//啟動(dòng)游戲線程
game.start();
while(true){
System.out.println(Thread.currentThread().getName()+"聽(tīng)音樂(lè)!");
}
}
}


//游戲
class Game implements Runnable{
@Override
public void run() {
while (true) {
System.out.println(Thread.currentThread().getName()+"打游戲!");
}
}
}

1.2.3 繼承方式和實(shí)現(xiàn)方式的區(qū)別

  • 1)繼承方式是一個(gè)類(lèi)繼承了Thread后成為線程類(lèi)的子類(lèi),實(shí)現(xiàn)方式是一個(gè)類(lèi)實(shí)現(xiàn)Runnable接口,但是這個(gè)類(lèi)不是線程類(lèi),因?yàn)樵擃?lèi)沒(méi)有start等方法.
  • 2)啟動(dòng)的時(shí)候繼承方式直接調(diào)用自己的start方法,實(shí)現(xiàn)方式是借助了Thread中的start方法啟動(dòng)的,自身沒(méi)有start方法
  • 3)繼承方式調(diào)用的run方法是通過(guò)方法覆蓋,通過(guò)繼承方式實(shí)現(xiàn)的,運(yùn)行的時(shí)候先找子類(lèi),沒(méi)有最后才運(yùn)行父類(lèi)的run方法.實(shí)現(xiàn)方式是執(zhí)行Thread的run方法,而Thread中的run方法調(diào)用了實(shí)現(xiàn)類(lèi)中的run方法,使用過(guò)組合關(guān)系的方法調(diào)用實(shí)現(xiàn)的.

1.3實(shí)現(xiàn) Callable 接口

1.3.1使用Callable和FutureTask創(chuàng)建線程的實(shí)現(xiàn)步驟:

  • 1)定義一個(gè)Callable接口的實(shí)現(xiàn)類(lèi)
  • 2)創(chuàng)建Callable實(shí)現(xiàn)類(lèi)對(duì)象傳遞給FutureTask構(gòu)造器
  • 3)將FutureTask對(duì)象傳遞給Thread構(gòu)造器
  • 4)Thread對(duì)象調(diào)用start方法啟動(dòng)線程
  • 5)通過(guò)FutureTask對(duì)象的get方法獲取線程運(yùn)行的結(jié)果

注意:
???? Future就是對(duì)于具體的Runnable或者Callable任務(wù)的執(zhí)行結(jié)果進(jìn)行取消碱蒙、查詢是否完成荠瘪、獲取結(jié)果。必要時(shí)可以通過(guò)get方法獲取執(zhí)行結(jié)果赛惩,該方法會(huì)阻塞直到任務(wù)返回結(jié)果哀墓。

使用場(chǎng)景:使用多線程計(jì)算結(jié)果并返回該結(jié)果。

1.3.2需求:使用2個(gè)線程異步計(jì)算1-1000,000內(nèi)之和

實(shí)現(xiàn)代碼:

public class CallableDemo {
public static void main(String[] args) throws Exception  {
//1.創(chuàng)建并啟動(dòng)線程
Callable<Integer> call1 = new CallableImpl(0, 50000);
Callable<Integer> call2 = new CallableImpl(50001, 100000);


FutureTask<Integer> f1 = new FutureTask<>(call1);
FutureTask<Integer> f2 = new FutureTask<>(call2);


new Thread(f1).start();
new Thread(f2).start();
//2.獲取每一個(gè)線程的結(jié)果
int ret1 = f1.get();
int ret2 = f2.get();
int ret= ret1+ret2;
System.out.println(ret);
}
}
class CallableImpl implements Callable<Integer>{


private int min;
private int max;


public CallableImpl(int min, int max) {
this.min = min;
this.max = max;
}


@Override
public Integer call() throws Exception {
int sum = 0;
for (int i = min; i <= max; i++) {
sum+=i;
}
return sum;
}
}

1.3.3Callable和Runnable的區(qū)別如下:

  1. Callable定義的方法是call坊秸,而Runnable定義的方法是run麸祷。

  2. Callable的call方法可以有返回值澎怒,而Runnable的run方法不能有返回值褒搔。

  3. Callable的call方法可拋出異常,而Runnable的run方法不能拋出異常喷面。

注意:

????FutureTask為Runnable的實(shí)現(xiàn)類(lèi)
????FutureTask可以視為一個(gè)閉鎖(門(mén)閂)星瘾,因?yàn)橹挥挟?dāng)線程運(yùn)行完才會(huì)出現(xiàn)結(jié)果。

1.4使用線程池(Executor框架【后面詳細(xì)講解Executor框架】)

???? 線程池惧辈,顧名思義就是一個(gè)池子里面放了很多的線程琳状,我們用就將線程從里面拿出來(lái),使用完畢就放回去池子中盒齿。設(shè)計(jì)和數(shù)據(jù)庫(kù)連接池相似念逞,存在靜態(tài)工廠方法用于創(chuàng)建各種線程池。

操作步驟:

1)使用Executors工具類(lèi)中的靜態(tài)工廠方法用于創(chuàng)建線程池
??????newFixedThreadPool:創(chuàng)建可重用且固定線程數(shù)的線程池边翁,
??????newScheduledThreadPool:創(chuàng)建一個(gè)可延遲執(zhí)行或定期執(zhí)行的線程池
??????newCachedThreadPool:創(chuàng)建可緩存的線程池
2)使用execute方法啟動(dòng)線程
3)使用shutdown方法等待提交的任務(wù)執(zhí)行完成并后關(guān)閉線程翎承。

代碼演示如下:

public class Demo4 {
    public static void main(String[] args) {
        Executor executor = Executors.newFixedThreadPool(5);
        executor.execute(new Runnable() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName());
            }
        });
        ((ExecutorService) executor).shutdown();
    }
}

1.5 Spring實(shí)現(xiàn)多線程

????在Spring3之后,Spring引入了對(duì)多線程的支持符匾,如果你使用的版本在3.1以前叨咖,應(yīng)該還是需要通過(guò)傳統(tǒng)的方式來(lái)實(shí)現(xiàn)多線程的。從Spring3同時(shí)也是新增了Java的配置方式,而且Java配置方式也逐漸成為主流的Spring的配置方式甸各。

代碼演示如下:

導(dǎo)入的包:

<dependencies>
     <dependency>
          <groupId>org.springframework.boot</groupId>
          <artifactId>spring-boot-starter</artifactId>
          <version>2.1.0.RELEASE</version>
     </dependency>
</dependencies>

配置類(lèi):

@Configuration
@ComponentScan("cn.wolfcode")
@EnableAsync //允許使用異步任務(wù)
public class SpringConfig {}

服務(wù)類(lèi):

@Service
public class SpringService {
    @Async // 這里進(jìn)行標(biāo)注為異步任務(wù)垛贤,在執(zhí)行此方法的時(shí)候,會(huì)單獨(dú)開(kāi)啟線程來(lái)執(zhí)行
    public void dowork1() {
        for (int i = 0; i < 10; i++) {
            System.out.println(Thread.currentThread().getName() + "-->" + i);
        }
    }
    @Async
    public void dowork2() {
        for (int i = 0; i < 10; i++) {
            System.out.println(Thread.currentThread().getName() + "-->" + i);
        }
    }
}


測(cè)試類(lèi):

public class SpringThreadDemo {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig.class);
        SpringService bean = context.getBean(SpringService.class);
        bean.dowork1();
        bean.dowork2();
    }
}

注意:此時(shí)會(huì)出現(xiàn)一個(gè)DEBUG信息

image.png

在這里DEBUG信息不是什么錯(cuò)誤趣倾,不會(huì)影響代碼的正常運(yùn)行聘惦,其實(shí)可以不用管的,但是為什么出現(xiàn)這個(gè)問(wèn)題呢儒恋?

????Spring的定時(shí)任務(wù)調(diào)度器會(huì)通過(guò)BeanFactory.getBean的方法來(lái)嘗試獲取一個(gè)注冊(cè)過(guò)的TaskExecutor對(duì)象來(lái)做任務(wù)調(diào)度部凑,獲取不到TaskExecutor對(duì)象再嘗試找ScheduledExecutorService 對(duì)象,都找不到就報(bào)DEBUG信息碧浊。報(bào)錯(cuò)之后就找自己本身默認(rèn)的scheduler定時(shí)器對(duì)象涂邀,這個(gè)舉動(dòng)其實(shí)是做一個(gè)提醒作用,所以如果沒(méi)有強(qiáng)迫癥可以不用管它箱锐。

1.5.1解決Spring使用多線程的報(bào)錯(cuò)信息

強(qiáng)迫癥患者想要解決怎么辦比勉,三種方式:

1.在log4j文件中加入log4j.logger.org.springframework.scheduling = INFO(治標(biāo)不治本)
2.在本配置文件或者配置類(lèi)中設(shè)置一個(gè)bean
3.配置類(lèi)實(shí)現(xiàn)AsyncConfigurer接口并覆蓋其getAsyncExecutor方法

1.6 定時(shí)器

嚴(yán)格來(lái)說(shuō)定時(shí)器(Timer)不是線程,他只是調(diào)度線程的一種工具,它里面封裝了一個(gè)線程驹止,所以我們可以使用定時(shí)器來(lái)使用線程浩聋。

image.png
image.png

操作步驟:

1)創(chuàng)建Timer 對(duì)象
2)調(diào)用schedule方法
3)傳入TimerTask子類(lèi)對(duì)象

代碼演示如下:

Timer timer = new Timer();
timer.schedule(new TimerTask() {
    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            System.out.println(Thread.currentThread().getName() + "-->" + i);
        }


    }
}, 100, 100);

想獲取更多技術(shù)干貨,請(qǐng)前往叩丁狼官網(wǎng):http://www.wolfcode.cn/all_article.html

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末臊恋,一起剝皮案震驚了整個(gè)濱河市衣洁,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌抖仅,老刑警劉巖坊夫,帶你破解...
    沈念sama閱讀 211,290評(píng)論 6 491
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異撤卢,居然都是意外死亡环凿,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,107評(píng)論 2 385
  • 文/潘曉璐 我一進(jìn)店門(mén)放吩,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)智听,“玉大人,你說(shuō)我怎么就攤上這事渡紫〉酵疲” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 156,872評(píng)論 0 347
  • 文/不壞的土叔 我叫張陵惕澎,是天一觀的道長(zhǎng)莉测。 經(jīng)常有香客問(wèn)我,道長(zhǎng)集灌,這世上最難降的妖魔是什么悔雹? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,415評(píng)論 1 283
  • 正文 為了忘掉前任复哆,我火速辦了婚禮,結(jié)果婚禮上腌零,老公的妹妹穿的比我還像新娘梯找。我一直安慰自己,他們只是感情好益涧,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,453評(píng)論 6 385
  • 文/花漫 我一把揭開(kāi)白布锈锤。 她就那樣靜靜地躺著掂骏,像睡著了一般假栓。 火紅的嫁衣襯著肌膚如雪潘拱。 梳的紋絲不亂的頭發(fā)上修己,一...
    開(kāi)封第一講書(shū)人閱讀 49,784評(píng)論 1 290
  • 那天,我揣著相機(jī)與錄音只锭,去河邊找鬼闭专。 笑死拨扶,一個(gè)胖子當(dāng)著我的面吹牛鸽捻,可吹牛的內(nèi)容都是我干的呼巴。 我是一名探鬼主播,決...
    沈念sama閱讀 38,927評(píng)論 3 406
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼御蒲,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼衣赶!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起厚满,我...
    開(kāi)封第一講書(shū)人閱讀 37,691評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤府瞄,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后碘箍,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體遵馆,經(jīng)...
    沈念sama閱讀 44,137評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,472評(píng)論 2 326
  • 正文 我和宋清朗相戀三年敲街,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了团搞。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,622評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡多艇,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出像吻,到底是詐尸還是另有隱情峻黍,我是刑警寧澤,帶...
    沈念sama閱讀 34,289評(píng)論 4 329
  • 正文 年R本政府宣布拨匆,位于F島的核電站姆涩,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏惭每。R本人自食惡果不足惜骨饿,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,887評(píng)論 3 312
  • 文/蒙蒙 一亏栈、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧宏赘,春花似錦绒北、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,741評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至贴汪,卻和暖如春脐往,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背扳埂。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,977評(píng)論 1 265
  • 我被黑心中介騙來(lái)泰國(guó)打工业簿, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人阳懂。 一個(gè)月前我還...
    沈念sama閱讀 46,316評(píng)論 2 360
  • 正文 我出身青樓辖源,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親希太。 傳聞我的和親對(duì)象是個(gè)殘疾皇子克饶,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,490評(píng)論 2 348

推薦閱讀更多精彩內(nèi)容

  • 進(jìn)程和線程 進(jìn)程 所有運(yùn)行中的任務(wù)通常對(duì)應(yīng)一個(gè)進(jìn)程,當(dāng)一個(gè)程序進(jìn)入內(nèi)存運(yùn)行時(shí),即變成一個(gè)進(jìn)程.進(jìn)程是處于運(yùn)行過(guò)程中...
    勝浩_ae28閱讀 5,087評(píng)論 0 23
  • 進(jìn)程和線程 進(jìn)程 所有運(yùn)行中的任務(wù)通常對(duì)應(yīng)一個(gè)進(jìn)程,當(dāng)一個(gè)程序進(jìn)入內(nèi)存運(yùn)行時(shí),即變成一個(gè)進(jìn)程.進(jìn)程是處于運(yùn)行過(guò)程中...
    小徐andorid閱讀 2,799評(píng)論 3 53
  • 下面是我自己收集整理的Java線程相關(guān)的面試題,可以用它來(lái)好好準(zhǔn)備面試誊辉。 參考文檔:-《Java核心技術(shù) 卷一》-...
    阿呆變Geek閱讀 14,762評(píng)論 14 507
  • 在有薄霧的早晨 我打著哈欠推開(kāi)古色古香的雕花窗戶 吱的一聲響 嚇飛了一群肥嘟嘟的幼鳥(niǎo) 大片大片濃麗的月季肆意盛開(kāi) ...
    蜜糖陶閱讀 371評(píng)論 0 2
  • 這是一篇文章堕澄,寫(xiě)我的大學(xué)室友邀跃,在一起相處了兩年的人,遇到事情才知道她們有多么的心胸狹窄蛙紫。 近來(lái)不知是因?yàn)槭裁词虑椋?..
    _終歸閱讀 255評(píng)論 2 1