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

Java线程同步语句原理及使用疑问解析

Java内置锁相关疑问解答

背景

正在学习Java内置锁(Intrinsic Lock)相关内容,已掌握synchronized方法、同步语句、synchronized静态方法的基础区别,现针对以下问题寻求解答:

疑问1:同步语句避免同步调用外部方法的逻辑示例

教程提到addName方法需要同步lastName和nameCount的修改,但要避免在同步块内调用其他对象的方法(否则可能引发活性问题),如果不用同步语句就得单独写非同步方法调用nameList.add。下面用代码示例拆解这个逻辑:

错误示例(可能引发活性问题)

public class NameManager {
    private List<String> nameList = new ArrayList<>();
    private String lastName;
    private int nameCount;

    // 同步方法:整个方法都持有当前对象锁
    public synchronized void addName(String name) {
        lastName = name;
        nameCount++;
        // 调用外部对象(nameList)的方法,此时仍持有当前对象锁
        // 如果nameList的add方法内部也持有锁,或其他线程在等待nameList的锁,可能导致死锁或长时间阻塞
        nameList.add(name);
    }
}

这里的问题是:addName作为同步方法,整个执行过程都持有NameManager对象的锁,调用nameList.add时,若nameList是多线程访问的同步集合(比如Vector),或其他线程在等待nameList的锁,就可能出现锁嵌套导致的死锁,或当前线程长时间持有NameManager的锁,导致其他需要该锁的线程无法执行,引发饥饿等活性问题。

正确示例(用同步语句拆分锁范围)

public class NameManager {
    private List<String> nameList = new ArrayList<>();
    private String lastName;
    private int nameCount;

    public void addName(String name) {
        // 仅在修改共享变量lastName和nameCount时持有当前对象锁
        synchronized (this) {
            lastName = name;
            nameCount++;
        }
        // 调用外部对象方法时,已释放当前对象锁,避免锁持有时间过长或嵌套
        nameList.add(name);
    }
}

这个写法里,同步块只包裹需要保护的共享变量修改逻辑,执行完就释放锁,再调用nameList.add。这样即使nameList的操作耗时或涉及其他锁,也不会占用NameManager的锁,减少了锁竞争概率,避免活性问题。

疑问2:MsLunch类细粒度同步的深层逻辑

教程里的MsLunch类用两个独立锁分别保护无关的c1、c2字段,不只是简单的“独立锁对应变量”,深层逻辑包括:

  • 减少锁竞争:如果用同一个锁保护所有字段,操作无关c1和c2的线程也需要等待同一锁,并发度极低。用独立锁后,操作c1和c2的线程互不干扰,能同时执行,提升并发吞吐量。
  • 降低锁持有时间:细粒度锁只会在操作对应字段时持有锁,不需要在整个方法执行期间持有,减少了其他线程等待锁的时间。
  • 避免锁膨胀风险:如果一个锁被频繁竞争,JVM可能会把偏向锁升级为轻量级锁,再升级为重量级锁,性能下降。细粒度锁的竞争概率低,能维持更高效的锁状态。
  • 逻辑清晰易维护:每个锁只负责保护一组相关的共享状态,符合单一职责原则,便于排查并发问题。

同步语句与同步方法的效率对比测试代码

下面是一个简单的对比测试,模拟多线程对两个独立共享变量的操作:

测试类定义

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class SyncCompareTest {
    // 用同步方法的测试类
    static class SyncMethodTest {
        private int count1 = 0;
        private int count2 = 0;

        public synchronized void incrementCount1() {
            count1++;
        }

        public synchronized void incrementCount2() {
            count2++;
        }
    }

    // 用同步语句的测试类
    static class SyncStatementTest {
        private int count1 = 0;
        private int count2 = 0;
        private Object lock1 = new Object();
        private Object lock2 = new Object();

        public void incrementCount1() {
            synchronized (lock1) {
                count1++;
            }
        }

        public void incrementCount2() {
            synchronized (lock2) {
                count2++;
            }
        }
    }

    public static void main(String[] args) throws InterruptedException {
        int threadNum = 100;
        int loopNum = 100000;

        // 测试同步方法
        SyncMethodTest methodTest = new SyncMethodTest();
        CountDownLatch latch1 = new CountDownLatch(threadNum * 2);
        ExecutorService executor1 = Executors.newFixedThreadPool(threadNum);
        long start1 = System.currentTimeMillis();

        for (int i = 0; i < threadNum; i++) {
            executor1.submit(() -> {
                for (int j = 0; j < loopNum; j++) {
                    methodTest.incrementCount1();
                }
                latch1.countDown();
            });
            executor1.submit(() -> {
                for (int j = 0; j < loopNum; j++) {
                    methodTest.incrementCount2();
                }
                latch1.countDown();
            });
        }
        latch1.await();
        long end1 = System.currentTimeMillis();
        executor1.shutdown();
        System.out.println("同步方法耗时:" + (end1 - start1) + "ms");

        // 测试同步语句
        SyncStatementTest statementTest = new SyncStatementTest();
        CountDownLatch latch2 = new CountDownLatch(threadNum * 2);
        ExecutorService executor2 = Executors.newFixedThreadPool(threadNum);
        long start2 = System.currentTimeMillis();

        for (int i = 0; i < threadNum; i++) {
            executor2.submit(() -> {
                for (int j = 0; j < loopNum; j++) {
                    statementTest.incrementCount1();
                }
                latch2.countDown();
            });
            executor2.submit(() -> {
                for (int j = 0; j < loopNum; j++) {
                    statementTest.incrementCount2();
                }
                latch2.countDown();
            });
        }
        latch2.await();
        long end2 = System.currentTimeMillis();
        executor2.shutdown();
        System.out.println("同步语句耗时:" + (end2 - start2) + "ms");
    }
}

测试结果说明

运行后会发现,同步语句的耗时明显少于同步方法:同步方法中两个increment方法共享同一个对象锁,线程操作count1和count2时互相阻塞;而同步语句用两个独立锁,操作count1和count2的线程可以同时执行,并发效率更高。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.06 19:16:30