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

如何使用synchronized块或锁保证变量可见性?附代码示例

如何用synchronized块或锁保证变量可见性?

你给出的示例代码无法终止,核心原因是线程对keepRunning的修改没有同步到主内存,循环线程一直读取本地缓存的旧值。虽然volatile或AtomicBoolean能解决问题,但用synchronized或Lock同样可以实现,关键是要让读写操作都基于同一个监视器/锁,触发内存同步语义。

一、用synchronized块实现的正确代码

public class TestTestTest {

    private void setFalseFlag() {
        // 修改变量时加锁,确保写入刷新到主内存
        synchronized (this) {
            this.keepRunning = false;
        }
        System.out.println("keepRunning is false");
    }

    private boolean keepRunning = true;

    public static void main(String[] args) throws InterruptedException {
        TestTestTest t = new TestTestTest();
        Thread startLoop = new Thread(() -> {
            System.out.println("before loop");
            while (true) {
                // 每次循环都加锁读取,确保从主内存获取最新值
                synchronized (t) {
                    if (!t.keepRunning) {
                        break;
                    }
                }
            }
        });
        Thread flagChanger = new Thread(() -> {
            System.out.println("before setting flag");
            t.setFalseFlag();
        });
        startLoop.start();
        Thread.sleep(1000);
        flagChanger.start();
    }
}

原理说明:

  • 进入synchronized块时,线程会失效本地处理器缓存,强制从主内存重新加载变量值;
  • 退出synchronized块时,会把线程的本地修改刷新到主内存;
  • 两个线程都用t实例(也就是this)作为监视器,所以修改后的keepRunning能被循环线程实时感知到。

二、用Lock接口实现的正确代码

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class TestTestTest {

    private final Lock lock = new ReentrantLock();

    private void setFalseFlag() {
        lock.lock();
        try {
            this.keepRunning = false;
        } finally {
            lock.unlock();
        }
        System.out.println("keepRunning is false");
    }

    private boolean keepRunning = true;

    public static void main(String[] args) throws InterruptedException {
        TestTestTest t = new TestTestTest();
        Thread startLoop = new Thread(() -> {
            System.out.println("before loop");
            while (true) {
                t.lock.lock();
                try {
                    if (!t.keepRunning) {
                        break;
                    }
                } finally {
                    t.lock.unlock();
                }
            }
        });
        Thread flagChanger = new Thread(() -> {
            System.out.println("before setting flag");
            t.setFalseFlag();
        });
        startLoop.start();
        Thread.sleep(1000);
        flagChanger.start();
    }
}

原理说明:

所有Lock实现都遵循和内置监视器锁相同的内存语义:成功的lock()操作对应monitorEnter,unlock()对应monitorExit,同样能触发缓存失效和主内存刷新,保证变量可见性。

关键注意点

  • 不能只在写入时加锁:如果循环线程不通过锁读取变量,它还是会用本地缓存的旧值,无法感知到修改;
  • 循环内要每次都获取锁:不能把锁放在while条件外,否则只会读取一次主内存值,后续循环还是用本地缓存。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.06 07:31:07