You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用join()等待线程执行却未得到预期结果的问题

问题分析与解决:join()后count值不符合预期的原因

核心误区

你以为调用t1.join()会让t2等t1执行完再启动,但实际上t1.start()和t2.start()是连续执行的——两个线程几乎同时开始运行。t1.join()只是让主线程等待t1执行完毕,但这时候t2早已经在后台执行count++操作了,所以第一个System.out.println(count)打印的是t1跑完的10000加上t2已经执行了一部分的结果,自然不是你预期的10000。

第二个问题:count++的原子性

就算你让线程按顺序启动,count++本身也不是原子操作——它拆分为三个步骤:

  1. 读取当前count的值
  2. 对值加1
  3. 将新值写回count

哪怕加了volatile,也只能保证线程间的可见性,无法避免多个线程同时执行这三步时出现竞态条件:比如两个线程同时读到同一个值,加1后写回,相当于只加了1次,最终count会小于20000。

两种修正方案

方案1:让线程真正按顺序启动

如果要让t1完全执行完再启动t2,把t2.start()放到t1.join()之后:

public class TestJoinMethod {
    volatile private int count =0;

    public static void main(String[] args) {
        TestJoinMethod testJoinMethod = new TestJoinMethod();
        testJoinMethod.execute();
    }

    public void execute() {
        Thread t1 = new Thread(new Runnable() {
            @Override
            public void run() {
                for(int i=0; i<10000; i++) {
                    count++;
                }
            }
        });

        Thread t2 = new Thread(new Runnable() {
            @Override
            public void run() {
                for(int i=0; i<10000; i++) {
                    count++;
                }
            }
        });

        t1.start();
        try {
            t1.join();
            System.out.println(count); // 此时输出10000
            t2.start();
            t2.join();
            System.out.println(count); // 此时输出20000
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
    }
}

方案2:用原子类保证count++的原子性

如果需要两个线程同时运行,但保证count的正确累加,可以用java.util.concurrent.atomic.AtomicInteger替换普通int,它的incrementAndGet()方法是原子操作:

import java.util.concurrent.atomic.AtomicInteger;

public class TestJoinMethod {
    private AtomicInteger count = new AtomicInteger(0);

    public static void main(String[] args) {
        TestJoinMethod testJoinMethod = new TestJoinMethod();
        testJoinMethod.execute();
    }

    public void execute() {
        Thread t1 = new Thread(new Runnable() {
            @Override
            public void run() {
                for(int i=0; i<10000; i++) {
                    count.incrementAndGet();
                }
            }
        });

        Thread t2 = new Thread(new Runnable() {
            @Override
            public void run() {
                for(int i=0; i<10000; i++) {
                    count.incrementAndGet();
                }
            }
        });

        t1.start();
        t2.start();

        try {
            t1.join();
            System.out.println(count.get()); // 此时t2可能还在跑,输出是t1完成的10000加上t2已执行的部分
            t2.join();
            System.out.println(count.get()); // 此时输出20000
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
    }
}

注意:方案2中第一个打印的结果依然可能不是10000,因为t2和t1是同时运行的,如果你需要第一个打印必须是10000,还是得用方案1的顺序启动方式。

内容的提问来源于stack exchange,提问作者nupur_s

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.01 17:50:36