线程安全的类 ≠ 线程安全的程序

java 复制代码
import java.util.Vector;

public class Demo20 {
    public static void main(String[] args) throws InterruptedException {
        Vector<String> v = new Vector<>();
        Thread t1 = new Thread(() -> {
            if(v.isEmpty()) {
                v.add("hello");
            }
        });

        Thread t2 = new Thread(() -> {
            if(v.isEmpty()) {
                v.add("hello");
            }
        });

        t1.start();
        t2.start();
        t1.join();
        t2.join();
        System.out.println(v);
    }
}

假设你修复了 join() 问题,现在考虑这个逻辑:

java 复制代码
if (v.isEmpty()) {   // ← 检查
   v.add("hello");  // ← 操作
}

虽然 isEmpty()add() 各自是线程安全的(Vector 内部加锁),但这两个操作合起来不是原子的

🧨 并发执行时的可能场景:
时间 线程 t1 线程 t2
t1 调用 v.isEmpty() → 返回 true ---
t2 --- 调用 v.isEmpty() → 返回 true
t3 执行 v.add("hello") ---
t4 --- 执行 v.add("hello")

→ 最终 v = ["hello", "hello"]

❌ 这违反了"只添加一次"的意图!

这就是 "复合操作非原子" 的经典问题。

✅ 为什么 Vector 的线程安全不够用?

  • Vector 保证的是:单个方法调用是原子的 (如 add()isEmpty()get() 等)。

  • 但它无法保证多个方法调用之间的逻辑是原子的

  • 这种"先判断再操作"的模式,需要外部同步

✅ 正确解决方案:对外层逻辑加锁(推荐)

java 复制代码
public class Demo20 {
    public static void main(String[] args) throws InterruptedException {
        Vector<String> v = new Vector<>();
        
        Thread t1 = new Thread(() -> {
            synchronized (v) { // 使用 Vector 自身作为锁
                if (v.isEmpty()) {
                    v.add("hello");
                }
            }
        });

        Thread t2 = new Thread(() -> {
            synchronized (v) {
                if (v.isEmpty()) {
                    v.add("hello");
                }
            }
        });

        t1.start();
        t2.start();
        t1.join();
        t2.join();
        
        System.out.println(v); // 输出: [hello] (只添加一次)
    }
}

✅ 因为 Vector 本身用 this 加锁,所以我们也可以用 synchronized(v) 来保护复合操作。

相关推荐
孟陬16 小时前
国外技术周刊 #1:Paul Graham 重新分享最受欢迎的文章《创作者的品味》、本周被划线最多 YouTube《如何在 19 分钟内学会 AI》、为何我不
java·前端·后端
想用offer打牌17 小时前
一站式了解四种限流算法
java·后端·go
华仔啊17 小时前
Java 开发千万别给布尔变量加 is 前缀!很容易背锅
java
也些宝18 小时前
Java单例模式:饿汉、懒汉、DCL三种实现及最佳实践
java
Nyarlathotep011318 小时前
SpringBoot Starter的用法以及原理
java·spring boot
wuwen518 小时前
WebFlux + Lettuce Reactive 中 SkyWalking 链路上下文丢失的修复实践
java
SimonKing19 小时前
GitHub 10万星的OpenCode,正在悄悄改变我们的工作流
java·后端·程序员
Seven9720 小时前
虚拟线程深度解析:轻量并发编程的未来趋势
java
雨中飘荡的记忆1 天前
ElasticJob分布式调度从入门到实战
java·后端