使用synchronized是否会使线程池中的代码变为顺序执行?
你说得对,在这个特定的场景下,你的观察确实抓住了核心问题——当前的代码设计下,加了synchronized的run方法确实会让所有任务串行执行,线程池的多线程能力完全没发挥出来,但我们可以拆解清楚背后的原因,以及如何优化这种场景。
一、为什么加了synchronized后会变成“串行执行”?
你给出的两段代码里,所有任务共享同一个Counter实例:
Counter task=new Counter(); for (int i=0;i<1000;i++ ) { exec.execute(task); }
而synchronized修饰实例方法run()时,锁的是当前Counter对象的监视器(object monitor)。也就是说,不管线程池有多少个线程,每次只有一个线程能获取到这个锁,执行count++操作;其他线程必须等当前线程释放锁后,才能竞争锁并执行。
这种情况下,1000个任务确实是一个接一个串行执行的,和直接用单线程循环1000次count++的效果几乎一致,线程池的多线程能力完全被浪费了。
二、什么时候线程池+synchronized才有意义?
线程池的价值在于并行执行互不依赖的任务,如果你的任务里只有一小部分逻辑需要同步,那线程池依然能发挥作用。比如修改一下run方法:
public void run(){ // 1. 这里是不需要同步的耗时操作,比如计算、IO等 long result = heavyCalculation(); // 2. 只有更新共享变量的部分需要同步 synchronized(this){ count++; } }
这种情况下,多个线程可以并行执行第一步的耗时操作,只有在更新count的时候才会串行等待锁,线程池的并行能力就体现出来了。
三、这个场景的更优解决方案
针对“多线程更新共享计数器”这个需求,其实不需要用synchronized,可以用Java并发包提供的AtomicInteger,它基于CAS(Compare-And-Swap)原子操作实现,不需要锁,性能更高,同时能保证线程安全:
import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; public class Counter implements Runnable { AtomicInteger count; public Counter() { count = new AtomicInteger(0); } public void run() { count.incrementAndGet(); // 原子自增操作 } public static void main(String[] args) throws InterruptedException { ExecutorService exec = Executors.newFixedThreadPool(2); Counter task = new Counter(); for (int i = 0; i < 1000; i++) { exec.execute(task); } exec.shutdown(); exec.awaitTermination(50L, TimeUnit.SECONDS); System.out.println(task.count.get()); } }
这种实现下,线程池的两个线程可以并行执行incrementAndGet()操作,不会有锁等待,真正发挥了多线程的优势。
总结
你的理解在当前代码场景下是正确的——因为任务完全依赖同一个对象锁,线程池的多线程无法并行工作。但这是任务设计的问题,不是线程池或synchronized本身的问题。如果调整任务逻辑,或者使用更适合的并发工具,就能让线程池发挥真正的价值。
内容的提问来源于stack exchange,提问作者user11129457

