杨斌
发布于 2026-04-15 / 1 阅读
0
0

Java多线程中的锁

Java 中之所以有这么多锁,根本原因就是多线程
当多个线程同时访问共享数据时,会产生三个经典问题——原子性、可见性、有序性。锁就是为了解决这些问题而诞生的工具。

下面用生活中的例子 + 代码来分别说明为什么需要锁,以及不同的锁解决什么问题。

假设你和其他9个朋友们共同有一个银行账户,余额 100 元。
你们俩同时去 ATM 取钱,每人取 10 元。正常逻辑:取完后余额应为 0 元。
但由于多线程并发,可能出现下面情况:

package com.thinkdifferent.aipicturebackend;

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

public class NoLockDemo {
    static int balance = 100;

    public static void withdraw(int amount) {
        if (balance >= amount) {
            // 模拟取钱过程中的耗时操作
            try { Thread.sleep(10); } catch (InterruptedException e) {}
            balance = balance - amount;
        }
    }

    public static void main(String[] args) throws InterruptedException {
        ThreadPoolExecutor executor = new ThreadPoolExecutor(
                5,                               // corePoolSize
                10,                               // maximumPoolSize
                60L,                             // keepAliveTime
                TimeUnit.SECONDS,                // 时间单位
                new ArrayBlockingQueue<>(10),    // 有界队列,避免内存爆炸
                Executors.defaultThreadFactory(), // 线程工厂
                new ThreadPoolExecutor.CallerRunsPolicy() // 拒绝策略
        );
        for (int i = 1; i <= 10; i++) {
            int taskid=i;
            executor.execute(() -> {
                withdraw(10);
                System.out.println(Thread.currentThread().getName() + " 进行取钱,余额为: "+balance+"任务id: "+taskid);
            });
        }
        executor.shutdown();
        Thread.sleep(1000);
        System.out.println("最终余额:" + balance); 
    }
}

结果:最终余额可能不是 0。
原因:线程同时读到 balance=100,都判断 >=10 成立,然后各自减 10,后执行的线程覆盖了前一个的结果,导致只减了一次。

这就是原子性被破坏:balance - amount 不是一步完成的,它分“读-改-写”三步,线程在中间被打断。


评论