你應該會的一道多線程筆試題

前言

最近也面了好多家企業(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線程輸出BC線程輸出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 相應的線程類即可。

  • 采用implements Runnable 方式

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

    • 缺點:編程稍微復雜,如果要訪問當前線程,必須使用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)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

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

友情鏈接更多精彩內(nèi)容