分享一道美美美团面试题!
废话
朋友去美团面试,考了他一道纯工程的题目:
使用多线程实现 1 + 2 + 3 + ..... + 1000
确实是很好的一道面试题,相比于烂俗的链表反转,二分查找。这道题要有趣的多。

题目
多线程解决问题,首先的思路是把任务拆分。在这个题中,很容易想到两种拆分方式(我们以拆成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

本文使用 mdnice 排版
欢迎关注公众号:Coder编程
分类:
笔试 && 面试 | 学习系列
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】博客园2025新款「AI繁忙」系列T恤上架,前往周边小店选购
【推荐】凌霞软件回馈社区,携手博客园推出1Panel与Halo联合会员
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 解锁.NET 9性能优化黑科技:从内存管理到Web性能的最全指南
· 通过一个DEMO理解MCP(模型上下文协议)的生命周期
· MySQL下200GB大表备份,利用传输表空间解决停服发版表备份问题
· 记一次 .NET某固高运动卡测试 卡慢分析
· 微服务架构学习与思考:微服务拆分的原则
· AI浏览器自动化实战
· 解锁.NET 9性能优化黑科技:从内存管理到Web性能的最全指南
· Chat to MySQL 最佳实践:MCP Server 服务调用
· .NET周刊【3月第5期 2025-03-30】
· 重生之我是操作系统(八)----文件管理(上)