你應(yīng)該會的一道多線程筆試題

前言

最近也面了好多家企業(yè)甩苛,也總結(jié)到很多筆試經(jīng)驗和面試經(jīng)驗。筆試大多數(shù)Java題目都是徘握荆客網(wǎng)原題和簡單排序讯蒲,數(shù)據(jù)庫,Java基礎(chǔ)概念肄扎,數(shù)據(jù)結(jié)構(gòu)墨林,MVC模式等。面試官問的題目涉及的知識無非是Java基礎(chǔ)知識反浓,設(shè)計模式萌丈,網(wǎng)絡(luò)等赞哗。我發(fā)現(xiàn)出現(xiàn)頻率很高的知識點有多線程雷则,設(shè)計模式(單例模式,策略模式肪笋,觀察者模式)等月劈。今天就來說一下筆試和面試中常見的多線程題目。

dream.jpg

筆試

  • 題目:有A藤乙,B猜揪,C三個線程,,A線程輸出A坛梁,B線程輸出B而姐, C線程輸出C,要求划咐,同時啟動三個線程,拴念,按順序輸出ABC钧萍,循環(huán)10次。這道題目出現(xiàn)的頻率很高啊政鼠。
第一種思路
  • 創(chuàng)建3個線程輪流輸出风瘦,用lock對象去同步線程的狀態(tài),用count變量標識出哪個線程公般,MAX變量用于邊界控制万搔,適時退出輪詢。(沒有用到wait()和notify()線程通信機制)

  • 手寫代碼

public class PrintABC {

    public static void main(String[] args) {
        final Lock lock = new ReentrantLock();
        Thread a = new Thread(new PrintfABCThread("A", lock, 0));
        Thread b = new Thread(new PrintfABCThread("B", lock, 1));
        Thread c = new Thread(new PrintfABCThread("C", lock, 2));

        a.start();
        b.start();
        c.start();
    }
}

class PrintfABCThread implements Runnable {
    private String name;
    private Lock lock;
    private Integer flag;

    public static int count = 0;

    public static final int MAX = 30;

    public PrintfABCThread(String name, Lock lock, Integer flag) {
        this.name = name;
        this.lock = lock;
        this.flag = flag;
    }

    @Override
    public void run() {
        while (true) {
            lock.lock();

            if (count >= MAX) {
                lock.unlock();
                return;
            }

            if (count % 3 == flag) {
                System.out.println(name);
                count++;
            }
            lock.unlock();
        }
    }
}
  • 輸出結(jié)果


    image.png
第二種思路
  • 通過Thread類的join()方法讓我們開啟的線程加入到主線程官帘,只有我們開啟的新線程結(jié)束后瞬雹,主線程才能繼續(xù)執(zhí)行。(不滿足題意刽虹,創(chuàng)建了30個線程挖炬,而且沒有同時開啟線程)

  • 手寫代碼

public class PrintfABC {

    public static void main(String[] args) throws InterruptedException {
        for (int i = 0; i < 10; i++) {
            Thread a = new Thread(new PrintThread("A"));
            a.start();
            a.join();
            Thread b = new Thread(new PrintThread("B"));
            b.start();
            b.join();
            Thread c = new Thread(new PrintThread("C"));
            c.start();
            c.join();
        }
    }
}

class PrintThread implements Runnable {
    private String name;

    public PrintThread(String name) {
        this.name = name;
    }

    @Override
    public void run() {
        System.out.println(name);
    }
}
  • 輸出結(jié)果


    image.png
第三種思路
  • 定義一個MainLock繼承于ReentrantLock,里面維護著3個condition状婶,用于線程之間的通信意敛。
public class MainLock extends ReentrantLock {

    private static final long serialVersionUID = 7103258623232795241L;

    private int count = 0;

    private final int max;

    private final Condition a;

    private final Condition b;

    private final Condition c;

    public MainLock(int max) {
        this.max = max;
        this.a = this.newCondition();
        this.b = this.newCondition();
        this.c = this.newCondition();
    }

    public boolean isEnd() {
        if (count >= max) {
            return true;
        }

        return false;
    }

    public void increase() {
        count++;
    }

    public int getCount() {
        return this.count;
    }

    public int getMax() {
        return this.max;
    }

    public Condition getA() {
        return this.a;
    }

    public Condition getB() {
        return this.b;
    }

    public Condition getC() {
        return this.c;
    }

    public boolean isA() {
        return count % 3 == 0;
    }

    public boolean isB() {
        return count % 3 == 1;
    }

    public boolean isC() {
        return count % 3 == 2;
    }
}

  • 創(chuàng)建一個定長線程池,開啟3個線程膛虫,分別去處理輸出A草姻,B,C的請求稍刀。
public class Main {

    public static void main(String[] args) {
        MainLock lock = new MainLock(30);
        ExecutorService pool = Executors.newFixedThreadPool(3);
        pool.submit(new AThread(lock));
        pool.submit(new BThread(lock));
        pool.submit(new CThread(lock));

        pool.shutdown();
    }
}

class AThread implements Runnable {
    private final MainLock lock;

    public AThread(MainLock lock) {
        this.lock = lock;
    }

    @Override
    public void run() {
        while (true) {
            lock.lock();
            try {
                if (lock.isA()) {
                    if (lock.isEnd()) {
                        System.exit(1);
                    } else {
                        print();
                    }
                    lock.increase();
                    lock.getB().signal();
                } else {
                    try {
                        lock.getA().await();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            } finally {
                lock.unlock();
            }
        }
    }

    private void print() {
        System.out.println("A ");
    }
}

class BThread implements Runnable {
    private final MainLock lock;

    public BThread(MainLock lock) {
        this.lock = lock;
    }

    @Override
    public void run() {
        while (true) {
            lock.lock();
            try {
                if (lock.isB()) {
                    if (lock.isEnd()) {
                        System.exit(1);
                    } else {
                        print();
                    }
                    lock.increase();
                    lock.getC().signal();
                } else {
                    try {
                        lock.getB().await();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            } finally {
                lock.unlock();
            }
        }
    }

    private void print() {
        System.out.println("B ");
    }
}

class CThread implements Runnable {
    private final MainLock lock;

    public CThread(MainLock lock) {
        this.lock = lock;
    }

    @Override
    public void run() {
        while (true) {
            lock.lock();
            try {
                if (lock.isC()) {
                    if (lock.isEnd()) {
                        System.exit(1);
                    } else {
                        print();
                    }
                    lock.increase();
                    lock.getA().signal();
                } else {
                    try {
                        lock.getC().await();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            } finally {
                lock.unlock();
            }
        }
    }

    private void print() {
        System.out.println("C ");
    }
}
  • 輸出結(jié)果


    image.png

  • 第二個題目: 用多線程去處理"abc"撩独,"def"“ghi”這個三個字符串账月,讓它們以"adg"综膀,"beh"“cfi”這種形式輸出局齿。這個題目之前是紅星美凱龍技術(shù)部筆試卷的壓軸題剧劝,分值是20分。
第一種思路

其實跟第一個題目的解決思路是差不多抓歼,唯一變的就是我們要獲取下標訪問字符串從而獲取字符讥此。我們可以通過count變量來標識由哪一個線程輸出,通過count / 3 獲取下標谣妻。(還是沒有用到wait()和notify()機制)

public class DemoTwo {

    public static void main(String[] args) {
        final Lock lock = new ReentrantLock();
        Thread a = new Thread(new PrintThread("abc", lock, 0));
        Thread b = new Thread(new PrintThread("def", lock, 1));
        Thread c = new Thread(new PrintThread("ghi", lock, 2));

        a.start();
        b.start();
        c.start();
    }
}

class PrintThread implements Runnable {
    private String name;
    private Lock lock;
    private Integer flag;

    public static int count = 0;

    public static int MAX = 9;

    public PrintThread(String name, Lock lock, Integer flag) {
        this.name = name;
        this.lock = lock;
        this.flag = flag;
    }

    @Override
    public void run() {
        while (true) {
            lock.lock();

            if (count >= MAX) {
                lock.unlock();
                return;
            }

            if (count % 3 == flag) {
                System.out.print(name.charAt(count / 3) + " ");
                count++;
            }

            lock.unlock();
        }
    }
}
  • 輸出結(jié)果萄喳。


    image.png
第二種思路
  • 和上面的思路是一樣的。(沒有同時開啟3個線程)

  • 手寫代碼

public class DemoOne {

    public static void main(String[] args) throws InterruptedException {
        for (int i = 0; i < 3; i++) {
            Thread a = new Thread(new MyThread("abc", i));
            a.start();
            a.join();

            Thread b = new Thread(new MyThread("def", i));
            b.start();
            b.join();

            Thread c = new Thread(new MyThread("ghi", i));
            c.start();
            c.join();

            System.out.println("");
        }
    }
}

class MyThread implements Runnable {
    private String str;
    private int index;

    public MyThread(String str, int index) {
        this.str = str;
        this.index = index;
    }

    @Override
    public void run() {
        System.out.print(String.valueOf(str.charAt(index)) + " ");
    }
}
  • 輸出結(jié)果蹋半。


    image.png
第三種思路
public class Main3 {

    public static void main(String args[]) {
        MainLock lock = new MainLock(9);
        ExecutorService pool = Executors.newFixedThreadPool(3);
        pool.submit(new XThread(lock));
        pool.submit(new YThread(lock));
        pool.submit(new ZThread(lock));

        pool.shutdown();
    }
}

class XThread implements Runnable {
    private final MainLock lock;

    public XThread(MainLock lock) {
        this.lock = lock;
    }

    @Override
    public void run() {
        while (true) {
            lock.lock();
            try {
                if (lock.isA()) {
                    if (lock.isEnd()) {
                        System.exit(1);
                    } else {
                        print();
                    }
                    lock.increase();
                    lock.getB().signal();
                } else {
                    try {
                        lock.getA().await();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            } finally {
                lock.unlock();
            }
        }
    }

    private void print() {
        System.out.print("abc".charAt(lock.getCount() / 3));
    }

}

class YThread implements Runnable {
    private final MainLock lock;

    public YThread(MainLock lock) {
        this.lock = lock;
    }

    @Override
    public void run() {
        while (true) {
            lock.lock();
            try {
                if (lock.isB()) {
                    if (lock.isEnd()) {
                        System.exit(1);
                    } else {
                        print();
                    }
                    lock.increase();
                    lock.getC().signal();
                } else {
                    try {
                        lock.getB().await();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            } finally {
                lock.unlock();
            }
        }
    }

    private void print() {
        System.out.print("def".charAt(lock.getCount() / 3));
    }

}

class ZThread implements Runnable {
    private final MainLock lock;

    public ZThread(MainLock lock) {
        this.lock = lock;
    }

    @Override
    public void run() {
        while (true) {
            lock.lock();
            try {
                if (lock.isC()) {
                    if (lock.isEnd()) {
                        System.exit(1);
                    } else {
                        print();
                    }
                    lock.increase();
                    lock.getA().signal();
                } else {
                    try {
                        lock.getC().await();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            } finally {
                lock.unlock();
            }
        }
    }

    private void print() {
        System.out.print("ghi".charAt(lock.getCount() / 3));
    }

}
  • 輸出結(jié)果


    image.png

面試

昨天去掃唄面試他巨,面試官問我多線程的實現(xiàn)的二種方式和彼此之間的區(qū)別。這個也很簡單,百度也爛大街了染突。

  • 采用extends Thread 方式

    • 優(yōu)點:編程簡單匪傍,如果要訪問當前線程,無需使用Thread.currentThread()方法觉痛,可以直接用this役衡,即可獲取當前線程。

    • 缺點:由于繼承了Thread,類無法再繼承其他的父類薪棒。

    • 使用方式:直接new 相應(yīng)的線程類即可手蝎。

  • 采用implements Runnable 方式

    • 優(yōu)點:沒有繼承Thread類,所以可以繼承其他的父類俐芯,在這種形式下棵介,多個線程可以共享同一個對象,所以非常合適多個相同的線程來處理同一份資源的情況下吧史,把cpu代碼和數(shù)據(jù)分開邮辽,形成清晰的模型,較好的體現(xiàn)了面向?qū)ο蟮乃枷朊秤_m用場景吨述,比如賣票。

    • 缺點:編程稍微復(fù)雜钞脂,如果要訪問當前線程揣云,必須使用Thread.currentThread()方法。

    • 使用方式:不能直接創(chuàng)建所需類的對象并運行它冰啃,而是必須從Thread類的一個實例內(nèi)部啟動它邓夕。

    public Thread(Runnable target) {
        init(null, target, "Thread-" + nextThreadNum(), 0);
    }

尾言

就算失望不能絕望,明天又去面試阎毅,美滋滋焚刚。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市扇调,隨后出現(xiàn)的幾起案子矿咕,更是在濱河造成了極大的恐慌,老刑警劉巖肃拜,帶你破解...
    沈念sama閱讀 206,968評論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件痴腌,死亡現(xiàn)場離奇詭異雌团,居然都是意外死亡燃领,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,601評論 2 382
  • 文/潘曉璐 我一進店門锦援,熙熙樓的掌柜王于貴愁眉苦臉地迎上來猛蔽,“玉大人,你說我怎么就攤上這事÷猓” “怎么了区岗?”我有些...
    開封第一講書人閱讀 153,220評論 0 344
  • 文/不壞的土叔 我叫張陵,是天一觀的道長毁枯。 經(jīng)常有香客問我慈缔,道長,這世上最難降的妖魔是什么种玛? 我笑而不...
    開封第一講書人閱讀 55,416評論 1 279
  • 正文 為了忘掉前任藐鹤,我火速辦了婚禮,結(jié)果婚禮上赂韵,老公的妹妹穿的比我還像新娘娱节。我一直安慰自己,他們只是感情好祭示,可當我...
    茶點故事閱讀 64,425評論 5 374
  • 文/花漫 我一把揭開白布肄满。 她就那樣靜靜地躺著,像睡著了一般质涛。 火紅的嫁衣襯著肌膚如雪稠歉。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,144評論 1 285
  • 那天汇陆,我揣著相機與錄音轧抗,去河邊找鬼。 笑死瞬测,一個胖子當著我的面吹牛横媚,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播月趟,決...
    沈念sama閱讀 38,432評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼灯蝴,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了孝宗?” 一聲冷哼從身側(cè)響起穷躁,我...
    開封第一講書人閱讀 37,088評論 0 261
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎因妇,沒想到半個月后问潭,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,586評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡婚被,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,028評論 2 325
  • 正文 我和宋清朗相戀三年狡忙,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片址芯。...
    茶點故事閱讀 38,137評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡灾茁,死狀恐怖窜觉,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情北专,我是刑警寧澤禀挫,帶...
    沈念sama閱讀 33,783評論 4 324
  • 正文 年R本政府宣布,位于F島的核電站拓颓,受9級特大地震影響语婴,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜驶睦,卻給世界環(huán)境...
    茶點故事閱讀 39,343評論 3 307
  • 文/蒙蒙 一腻格、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧啥繁,春花似錦菜职、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,333評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至适室,卻和暖如春嫡意,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背捣辆。 一陣腳步聲響...
    開封第一講書人閱讀 31,559評論 1 262
  • 我被黑心中介騙來泰國打工蔬螟, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人汽畴。 一個月前我還...
    沈念sama閱讀 45,595評論 2 355
  • 正文 我出身青樓旧巾,卻偏偏與公主長得像,于是被迫代替她去往敵國和親忍些。 傳聞我的和親對象是個殘疾皇子鲁猩,可洞房花燭夜當晚...
    茶點故事閱讀 42,901評論 2 345

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

  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 171,520評論 25 707
  • 本文主要講了java中多線程的使用方法、線程同步罢坝、線程數(shù)據(jù)傳遞廓握、線程狀態(tài)及相應(yīng)的一些線程函數(shù)用法、概述等嘁酿。 首先講...
    李欣陽閱讀 2,442評論 1 15
  • 前幾天無意間看了《中國有嘻哈》隙券,突然間一發(fā)不可收拾,里面的歌反復(fù)聽了很多遍闹司,不厭其煩娱仔。沒看之前,看著節(jié)目海報开仰,好像...
    艾米小姐閱讀 559評論 0 1
  • 文/攝:若木菡 楊柳舞金風拟枚,浮云卷碧空薪铜。 山由眉宇聚众弓,水是眼波融恩溅。 鷺渡圍屏里,童行畫頁中谓娃。 秋深琴更曲脚乡,彈與北飛...
    若木菡閱讀 484評論 28 25
  • Curator解決了很多Zookeeper客戶端非常底層的細節(jié)開發(fā)工作,包括連接重連滨达,反復(fù)注冊Watcher和No...
    米刀靈閱讀 1,561評論 0 0