一道美團(tuán)的面試題

大概是兩年前吧,朋友去美團(tuán)面試,考了他一道純工程的題目:

使用多線程實(shí)現(xiàn) 1 + 2 + 3 + ..... + 1000

確實(shí)是很好的一道面試題,這兩年面試別人,我也經(jīng)常會(huì)用這道題。相比于爛俗的鏈表反轉(zhuǎn),二分查找。這道題要有趣的多。

多線程解決問(wèn)題,首先的思路是把任務(wù)拆分。在這個(gè)題中,很容易想到兩種拆分方式(我們以拆成10份為例):
1 按范圍把1000個(gè)數(shù)分成10份,分別為
[1, 100]
[101, 200]
.....
[901, 1000]

2 每個(gè)數(shù)對(duì)10取模,分為10組
{1, 11, 21, 31.....991}
{2, 12, 22, .... 992}
....
{10, 20, 30, ..... 1000}

由于兩種方式?jīng)]有本質(zhì)的區(qū)別,為了簡(jiǎn)便,本文中使用第一種方式作為拆分方式實(shí)現(xiàn)。

1 最簡(jiǎn)單版本的實(shí)現(xiàn)

    public static void main(String[] args) throws InterruptedException {
        AtomicInteger sum = new AtomicInteger();
        List<Thread> threadList = new ArrayList<>();

        for (int i = 0; i < 10; i++) {
            final int cur = i;

            Thread thread = new Thread(new Runnable() {
                @Override
                public void run() {
                    for (int j = cur * 100 + 1; j <= cur * 100 + 100; j++) {
                        sum.addAndGet(j);
                    }
                }
            });
            thread.start();
            threadList.add(thread);
        }

        for (Thread thread : threadList) {
            thread.join();
        }

        System.out.println(sum.get());
    }

代碼沒(méi)啥亮點(diǎn),但也沒(méi)什么漏洞。
改進(jìn)點(diǎn)是可以使用線程池代替線程數(shù)組,使用CountDownLatch來(lái)判定結(jié)束。
改進(jìn)版的實(shí)現(xiàn)如下:

2 線程池版

    public static void main(String[] args) throws InterruptedException {
        AtomicInteger sum = new AtomicInteger();
        ExecutorService pool = Executors.newCachedThreadPool();
        final CountDownLatch latch = new CountDownLatch(10);

        for (int i = 0; i < 10; i++) {
            final int cur = i;

            pool.execute(new Runnable() {
                @Override
                public void run() {
                    for (int j = cur * 100 + 1; j <= cur * 100 + 100; j++) {
                        sum.addAndGet(j);
                    }

                    latch.countDown();
                }
            });
        }

        latch.await();
        System.out.println(sum.get());
    }

用到了線程池,一定會(huì)想到j(luò)ava的future類(lèi)和callable,正好適合解決這種問(wèn)題。

3 future版

    public static void main(String[] args) throws ExecutionException, InterruptedException {
        ExecutorService pool = Executors.newCachedThreadPool();
        List<Future<Integer>> futureList = new ArrayList<>();
        int sum = 0;

        for (int i = 0; i < 10; i++) {
            final int cur = i;

            futureList.add(pool.submit(new Callable<Integer>() {
                @Override
                public Integer call() {
                    int segSum = 0;

                    for (int j = cur * 100 + 1; j <= cur * 100 + 100; j++) {
                        segSum += j;
                    }

                    return segSum;
                }
            }));
        }

        for (int i = 0; i < 10; i++) {
            sum += futureList.get(i).get();
        }

        System.out.println(sum);
    }

上面的這幾種方式,都能正確得到答案,在工程上也基本沒(méi)有漏洞。擴(kuò)展性來(lái)說(shuō),也還算不錯(cuò)。
但總覺(jué)得中規(guī)中矩,沒(méi)有什么亮點(diǎn)。
不夠有趣。

前幾天看一篇分享,講stream.parallel的應(yīng)用場(chǎng)景。突然靈光一閃,發(fā)現(xiàn)用在這個(gè)問(wèn)題上簡(jiǎn)直是量身定做一般。
代碼如下:

    public static void main(String[] args) {
        System.out.println(IntStream.range(0, 10)
                .boxed()
                .parallel()
                .mapToInt(i -> IntStream.rangeClosed(i * 100 + 1, i * 100 + 100).sum())
                .sum());
    }

其實(shí)還可以更簡(jiǎn)化的:

    public static void main(String[] args) {
        System.out.println(IntStream.rangeClosed(0, 1000)
                .parallel()
                .sum());
    }

have fun

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

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

  • 不足的地方請(qǐng)大家多多指正,如有其它沒(méi)有想到的常問(wèn)面試題請(qǐng)大家多多評(píng)論,一起成長(zhǎng),感謝!~ String可以被繼承嗎...
    啟示錄是真的閱讀 3,065評(píng)論 3 3
  • 所有知識(shí)點(diǎn)已整理成app app下載地址 J2EE 部分: 1.Switch能否用string做參數(shù)? 在 Jav...
    侯蛋蛋_閱讀 2,700評(píng)論 1 4
  • 本文出自 Eddy Wiki ,轉(zhuǎn)載請(qǐng)注明出處:http://eddy.wiki/interview-java.h...
    eddy_wiki閱讀 2,297評(píng)論 0 14
  • 下面是Java線程相關(guān)的熱門(mén)面試題,你可以用它來(lái)好好準(zhǔn)備面試。 1) 什么是線程? 線程是操作系統(tǒng)能夠進(jìn)行運(yùn)算調(diào)度...
    冰箱哥哥閱讀 553評(píng)論 0 2
  • 不管你是新程序員還是老手,你一定在面試中遇到過(guò)有關(guān)線程的問(wèn)題。Java語(yǔ)言一個(gè)重要的特點(diǎn)就是內(nèi)置了對(duì)并發(fā)的支持,讓...
    堯淳閱讀 1,664評(píng)論 0 25

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