为何Counting类count值为0而非预期20?如何修正该多线程问题
问题分析与解决方案
哈哈,这个问题我刚学Java多线程的时候也踩过一模一样的坑!咱们拆解下问题出在哪,以及怎么拿到预期的20:
你的代码核心错误
- 每个线程操作的是独立的计数器实例:
mulA和mulB的run()方法里都各自new Counting(),这意味着两个线程完全在操作不同的Counting对象,各自把自己的计数器加到10,但这两个实例的状态完全和主线程最后打印的对象没关系。 - 最后打印的是全新的空计数器:main方法最后
new Counting()出来的obj4是一个全新的实例,它的count初始值就是0,所以打印0是必然的结果。 - 虽然
counter()方法加了synchronized,但这个锁是针对对象实例的,不同实例的锁互不影响,不过这里的核心问题根本不是锁,而是实例不共享。
修正方案:让两个线程共享同一个Counting实例
要拿到20,必须让两个线程操作同一个Counting对象,这样它们的累加才会作用在同一个count变量上。下面是两种常见的修正方式:
方式一:给Runnable实现类传入共享的Counting实例
修改你的代码,让mulA和mulB接收外部传入的Counting对象,而不是自己创建(还可以合并两个重复的Runnable类,减少冗余):
class Counting{ public int count ; public synchronized void counter(){ count = count + 1; } } // 合并成通用的Runnable实现,没必要写两个几乎一样的类 class CounterRunnable implements Runnable{ private Counting countingObj; // 持有共享的计数器实例 // 通过构造函数传入共享实例 public CounterRunnable(Counting countingObj) { this.countingObj = countingObj; } public void run() { for(int i = 0;i<10;i++) { countingObj.counter(); } } } public class MulTi { public static void main(String[] args) throws Exception { // 创建唯一的Counting实例,让两个线程共享 Counting sharedCounting = new Counting(); // 给两个Runnable传入同一个实例 CounterRunnable r1 = new CounterRunnable(sharedCounting); CounterRunnable r2 = new CounterRunnable(sharedCounting); Thread t1 = new Thread(r1); Thread t2 = new Thread(r2); t1.start(); t2.start(); t1.join(); t2.join(); // 打印共享实例的count值 System.out.println(sharedCounting.count); // 现在会输出20 } }
方式二:用静态变量实现共享(不推荐)
如果一定要保留原来的类结构,可以把Counting的count改成静态变量,这样所有实例共享同一个count值:
class Counting{ public static int count ; // 静态变量,所有实例共享 public synchronized void counter(){ count = count + 1; } } class mulA implements Runnable{ public void run() { Counting obj = new Counting(); for(int i = 0;i<10;i++) { obj.counter(); } } } class mulB implements Runnable{ public void run() { Counting obj1 = new Counting(); for(int i = 0;i<10;i++) { obj1.counter(); } } } public class MulTi { public static void main(String[] args) throws Exception { mulA obj2 = new mulA(); mulB obj3 = new mulB(); Thread t1 = new Thread(obj2); Thread t2 = new Thread(obj3); t1.start(); t2.start(); t1.join(); t2.join(); System.out.println(Counting.count); // 输出20 } }
不过这种方式不推荐,因为静态变量的线程安全管理更复杂,而且不符合面向对象的设计原则。
额外优化建议
其实count = count + 1这个操作,即使加了synchronized是没问题的,但如果用JUC包的原子类(比如AtomicInteger)可以更简洁地实现线程安全的累加,不需要手动加锁:
import java.util.concurrent.atomic.AtomicInteger; class Counting{ public AtomicInteger count = new AtomicInteger(0); public void counter(){ count.incrementAndGet(); // 原子操作,天然线程安全 } }
这样的写法更优雅,也避免了手动锁可能带来的问题。
内容的提问来源于stack exchange,提问作者nihit kumar
相关产品推荐
相关产品推荐

