分享一道美美美团面试题!

废话

朋友去美团面试,考了他一道纯工程的题目:

使用多线程实现 1 + 2 + 3 + ..... + 1000

确实是很好的一道面试题,相比于烂俗的链表反转,二分查找。这道题要有趣的多。

zb.jpg
zb.jpg

题目

多线程解决问题,首先的思路是把任务拆分。在这个题中,很容易想到两种拆分方式(我们以拆成10份为例): 1 .按范围把1000个数分成10份,分别为

[1, 100]
[101, 200]
.....
[901, 1000]

2 .每个数对10取模,分为10组

{1, 11, 21, 31.....991}
{2, 12, 22, .... 992}
....
{10, 20, 30, ..... 1000}

由于两种方式没有本质的区别,为了简便,本文中使用第一种方式作为拆分方式实现。

1 最简单版本的实现

    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());
    }

代码没啥亮点,但也没什么漏洞。 改进点是可以使用线程池代替线程数组,使用CountDownLatch来判定结束。 改进版的实现如下:

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());
    }

用到了线程池,一定会想到java的future类和callable,正好适合解决这种问题。

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);
    }

上面的这几种方式,都能正确得到答案,在工程上也基本没有漏洞。扩展性来说,也还算不错。 但总觉得中规中矩,没有什么亮点。 不够有趣。

前几天看一篇分享,讲stream.parallel的应用场景。突然灵光一闪,发现用在这个问题上简直是量身定做一般。 代码如下:

    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());
    }

其实还可以更简化的:

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

参考:https://www.jianshu.com/p/253cff3d895a

pzl.jpg
pzl.jpg

本文使用 mdnice 排版

posted @   Coder编程  阅读(696)  评论(0编辑  收藏  举报
编辑推荐:
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
阅读排行:
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· winform 绘制太阳,地球,月球 运作规律
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
点击右上角即可分享
微信分享提示