Java线程安全共享数据核心是控制访问,需通过同步机制(synchronized、ReentrantLock)、并发工具类(ConcurrentHashMap、AtomicXXX)、避免共享(ThreadLocal、不可变对象)及可见性保障(volatile、final、安全发布)综合实现。

Java中实现线程间安全共享数据,核心不是“能不能共享”,而是“如何控制访问”。关键在于避免多个线程同时读写同一份数据时出现脏读、丢失更新或不一致状态。以下从设计思路和常用手段两方面展开,直击实际开发中的关键点。
用同步机制串行化临界区操作
当多个线程需要修改同一对象的字段(如计数器、缓存项、状态标志),必须确保这些修改操作是原子且互斥的。
- synchronized关键字:最基础也最常用。可修饰实例方法(锁当前对象)、静态方法(锁Class对象)或代码块(指定任意Object为锁)。例如:synchronized(this) { count++; } 确保count++(读-改-写三步)不被中断。
- ReentrantLock:比synchronized更灵活,支持公平锁、可中断等待、超时获取、多条件队列等。需手动lock()/unlock(),推荐配合try-finally使用,防止死锁或资源泄漏。
优先选用线程安全的并发工具类
比起自己加锁,JDK提供的并发集合和原子类更高效、更不易出错,应作为首选方案。
- ConcurrentHashMap:替代HashMap用于多线程场景。它分段锁(JDK 8后改用CAS + synchronized细粒度锁),支持高并发读写,且迭代器弱一致性(不抛ConcurrentModificationException)。
- AtomicInteger / AtomicReference:适用于简单状态变更,如计数、开关、单个引用更新。底层基于CPU级别的CAS指令,无锁但保证可见性和原子性。
- CopyOnWriteArrayList:适合读多写少场景(如监听器列表)。写操作复制整个数组,读操作无锁,但内存开销大、实时性低,不适用于频繁写入。
避免共享——把数据“私有化”或“不可变”
最彻底的安全方式,是让线程不共享可变状态。
立即学习“Java免费学习笔记(深入)”;
- ThreadLocal:为每个线程提供独立副本。常见于用户上下文、数据库连接、格式化器(SimpleDateFormat)等。注意及时remove(),防止内存泄漏(尤其在线程池中)。
- 不可变对象(Immutable Object):如String、LocalDateTime、自定义final字段+无setter的对象。一旦创建就不能修改,天然线程安全,可自由共享。
- 栈封闭(Stack Confinement):变量只在方法内部创建和使用(如局部变量、方法参数),生命周期绑定线程栈,不会逃逸到其他线程。
明确共享边界与可见性保障
即使加了锁,若忽略内存模型,仍可能因指令重排或缓存不一致导致问题。
- volatile关键字:适用于“一个写、多个读”的简单状态标志(如running = false)。它禁止重排序,并强制线程每次读取主内存值,但不保证复合操作的原子性(如i++不行)。
- final字段的正确初始化:构造器中完成final字段赋值,能保证其他线程看到该对象时,final字段已正确初始化(安全发布)。
- 安全发布(Safe Publication):对象创建后要让其他线程可见,需通过同步机制(如synchronized、volatile引用、并发容器add、Static final等)发布,否则可能看到部分构造的对象。
基本上就这些。没有银弹,选哪种方式取决于具体场景:数据结构类型、读写比例、一致性要求、性能敏感度。多数情况下,先考虑ConcurrentHashMap或AtomicXXX;复杂逻辑再上锁;能不共享就不共享,用ThreadLocal或不可变对象更清爽。










