Java多线程中如何正确同步getter与run方法

来源:站长查询作者:小何头衔:草根站长
导读:本期聚焦于小伙伴创作的《Java多线程中如何正确同步getter与run方法》,敬请观看详情,探索知识的价值。以下视频、文章将为您系统阐述其核心内容与价值。如果您觉得《Java多线程中如何正确同步getter与run方法》有用,将其分享出去将是对创作者最好的鼓励。

在Java多线程编程中,多个线程同时操作共享资源时,如果读写操作没有做好同步控制,就可能出现数据不一致的问题。getter方法负责读取共享数据,run方法通常包含线程的核心执行逻辑,二者的同步配合是保障线程安全的关键。

Java多线程中如何正确同步getter与run方法

为什么要同步getter与run方法

当多个线程同时访问一个共享变量时,如果run方法中会修改这个变量,而getter方法会读取这个变量,没有同步的情况下就可能出现问题。比如线程A正在修改变量的值,修改到一半的时候线程B调用getter读取,就会拿到不完整的数据,也就是常说的脏读。

另外,Java内存模型中,每个线程有自己的工作内存,共享变量会存在主内存中。没有同步的话,线程可能不会及时把工作内存的变量刷新到主内存,或者其他线程无法感知变量的更新,导致读取到旧值。

run方法的同步实现

run方法是线程的执行入口,通常包含对共享资源的修改逻辑,需要根据场景选择同步方式。如果整个run方法的逻辑都需要保证原子性,可以直接给run方法加同步关键字,不过这种方式会限制线程的并发能力,更适合逻辑简单、执行时间短的场景。

更常见的做法是在run方法内部对操作共享资源的代码块加同步,这样其他不需要操作共享资源的逻辑可以并发执行,提升效率。下面是一个简单的示例,run方法内部修改共享计数变量:

public class CounterTask implements Runnable {
    private int count = 0;
    private final Object lock = new Object();

    @Override
    public void run() {
        // 不需要同步的耗时操作可以放在同步块外面
        long start = System.currentTimeMillis();
        // 同步修改共享变量的代码块
        synchronized (lock) {
            for (int i = 0; i < 1000; i++) {
                count++;
            }
        }
        long end = System.currentTimeMillis();
        System.out.println("线程执行耗时:" + (end - start) + "ms");
    }

    public int getCount() {
        return count;
    }
}

上面的代码中,lock是专门的锁对象,避免使用this作为锁对象,防止外部代码意外持有锁导致死锁。同步块只包裹修改count的代码,其他逻辑可以并发执行。

getter方法的同步策略

getter方法的同步需要根据共享变量的特性来决定。如果共享变量是基本类型,且run方法中的修改操作是原子的,那么可能不需要同步。比如int类型的赋值操作在Java中是原子的,但是如果是long或者double类型,在32位系统中赋值可能不是原子的,就需要同步。

如果共享变量是引用类型,或者run方法中的修改不是原子操作,那么getter方法也需要同步,保证读取到的是最新、完整的数据。同步getter的常用方式有两种:

1. 给getter方法加synchronized关键字

这种方式最简单,直接保证getter的同步,和run方法中使用的锁保持一致即可。示例代码如下:

public class SafeCounter {
    private int count = 0;
    private final Object lock = new Object();

    public void increment() {
        synchronized (lock) {
            count++;
        }
    }

    // getter方法加synchronized,使用同一个锁对象
    public synchronized int getCount() {
        synchronized (lock) {
            return count;
        }
    }
}

2. 使用volatile关键字修饰共享变量

如果共享变量的修改是原子操作,比如int类型的赋值,那么可以用volatile修饰变量,保证变量的可见性,这样getter方法不需要加锁也可以读取到最新值。但volatile不能保证复合操作的原子性,比如count++这种操作包含读取、修改、写入三步,volatile无法保证原子性,还是需要同步。

public class VolatileCounter {
    // volatile保证可见性,但是count++不是原子操作,还是需要同步修改逻辑
    private volatile int count = 0;
    private final Object lock = new Object();

    public void increment() {
        synchronized (lock) {
            count++;
        }
    }

    // getter不需要加锁,因为count是volatile的,读取的是最新值
    public int getCount() {
        return count;
    }
}

同步的注意事项

  • 锁对象要统一:run方法和getter方法必须使用同一个锁对象,否则同步会失效,两个方法还是可以并发执行,无法保证数据一致性。
  • 避免锁粒度过大:不要把不需要同步的逻辑放在同步块中,否则会降低多线程的并发效率,比如IO操作、耗时计算都不应该放在同步块里。
  • 优先使用专门的锁对象:不要直接使用this或者字符串作为锁对象,防止外部代码意外持有锁,导致死锁或者同步失效。
  • 复合操作必须同步:如果操作包含多个步骤,比如先判断再修改,即使getter做了同步,run方法中的复合操作也需要同步,否则还是会出现线程安全问题。

完整示例

下面是一个完整的多线程计数示例,run方法修改计数,getter方法读取计数,保证线程安全:

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

public class MultiThreadCounter {
    private int count = 0;
    private final Object lock = new Object();

    // run方法对应的任务逻辑
    public void runTask() {
        synchronized (lock) {
            for (int i = 0; i < 100; i++) {
                count++;
            }
        }
    }

    // 同步的getter方法
    public int getCount() {
        synchronized (lock) {
            return count;
        }
    }

    public static void main(String[] args) throws InterruptedException {
        MultiThreadCounter counter = new MultiThreadCounter();
        ExecutorService executor = Executors.newFixedThreadPool(5);
        // 提交10个任务,每个任务执行runTask
        for (int i = 0; i < 10; i++) {
            executor.submit(counter::runTask);
        }
        // 关闭线程池,等待所有任务执行完成
        executor.shutdown();
        executor.awaitTermination(1, TimeUnit.MINUTES);
        // 读取最终的计数结果
        System.out.println("最终计数结果:" + counter.getCount());
    }
}

上述代码中,所有修改count的操作都在同一个锁的同步块中,getter方法也使用同一个锁同步,因此最终读取到的计数是正确的,不会出现数据不一致的问题。

Java多线程getter同步run方法同步synchronized关键字线程安全修改时间:2026-07-02 15:33:20

免责声明:​ 已尽一切努力确保本网站所含信息的准确性。网站内容多为原创整理与精心编撰,观点力求客观中立。本站旨在免费分享,内容仅供个人学习、研究或参考使用。若引用了第三方作品,版权归原作者所有。如内容涉及您的权益,请联系我们处理。
内容垂直聚焦
专注技术核心技术栏目,确保每篇文章深度聚焦于实用技能。从代码技巧到架构设计,为用户提供无干扰的纯技术知识沉淀,精准满足专业提升需求。
知识结构清晰
覆盖从开发到部署的全链路。AI、前端、编程、数据库、服务器、建站、系统层层递进,构建清晰学习路径,帮助用户系统化掌握开发与运维所需的核心技术。
深度技术解析
拒绝泛泛而谈,深入技术细节与实践难点。无论是数据库优化还是服务器配置,均结合真实场景与代码示例进行剖析,致力于提供可直接应用于工作的解决方案。
专业领域覆盖
精准对应开发生命周期。从前端界面到后端编程,从数据库操作到服务器运维,形成完整闭环,一站式满足全栈工程师和运维人员的技术需求。
即学即用高效
内容强调实操性,步骤清晰、代码完整。用户可根据教程直接复现和应用于自身项目,显著缩短从学习到实践的距离,快速解决开发中的具体问题。
持续更新保障
专注既定技术方向进行长期、稳定的内容输出。确保各栏目技术文章持续更新迭代,紧跟主流技术发展趋势,为用户提供经久不衰的学习价值。