高并发场景中需要自定义工具类的场景,避免线程安全产生
在类属性设计中,确保线程安全的赋值方式有多种策略。以下是全面且实用的线程安全属性赋值方案:
-
基本原则和分类
不可变对象(最安全)
java
// 1. final 修饰的基础类型(确保多线程情况只能被复制一次)public class ImmutableConfig {
private final int maxConnections = 100; // 编译时常量
private final String apiUrl; // 构造时初始化
private final List<String> data = List.of("a", "b"); // 不可变集合public ImmutableConfig(String apiUrl) { this.apiUrl = apiUrl; }}
2. 各种场景下的线程安全赋值方案
方案A:volatile + 原子性操作
java
public class VolatileExample {
// volatile 确保可见性,但不保证复合操作的原子性
private volatile int counter;
private volatile boolean flag;
private volatile String message;// 简单的赋值是线程安全的 public void setMessage(String newMessage) { this.message = newMessage; // 线程安全 } // 非原子操作需要同步 public void incrementCounter() { counter++; // 非线程安全! }}
方案B:使用原子类
java
import java.util.concurrent.atomic.*;
public class AtomicExample {
// 原子整型
private final AtomicInteger counter = new AtomicInteger(0);
private final AtomicLong lastUpdateTime = new AtomicLong();
// 原子引用(适用于对象)
private final AtomicReference<User> currentUser = new AtomicReference<>();
private final AtomicReferenceArray<String> tags = new AtomicReferenceArray<>(10);
// 原子更新器(性能优化)
private static final AtomicIntegerFieldUpdater<AtomicExample>
visitsUpdater = AtomicIntegerFieldUpdater.newUpdater(
AtomicExample.class, "visits");
@SuppressWarnings("unused")
private volatile int visits; // 必须为volatile
public void updateUser(User newUser) {
User oldUser;
do {
oldUser = currentUser.get();
} while (!currentUser.compareAndSet(oldUser, newUser));
}
public void safeIncrement() {
counter.incrementAndGet();
visitsUpdater.incrementAndGet(this);
}
}
方案C:synchronized 同步
java
public class SynchronizedExample {
private int counter;
private List<String> dataList = new ArrayList<>();
private final Object lock = new Object(); // 专用锁对象
// 方法级别同步
public synchronized void increment() {
counter++;
}
public synchronized void addData(String item) {
dataList.add(item);
}
// 块级别同步(更细粒度)
public void updateCounter(int value) {
synchronized (lock) {
counter = value;
// 其他相关操作
}
}
// 双检查锁(DCL)单例模式
private volatile Config config;
public Config getConfig() {
if (config == null) {
synchronized (this) {
if (config == null) {
config = new Config();
}
}
}
return config;
}
}
方案D:Lock 接口
java
import java.util.concurrent.locks.*;
public class LockExample {
private int balance;
private final ReentrantLock lock = new ReentrantLock();
private final ReadWriteLock rwLock = new ReentrantReadWriteLock();
private final StampedLock stampedLock = new StampedLock();
// 普通锁
public void updateBalance(int amount) {
lock.lock();
try {
balance += amount;
// 其他操作
} finally {
lock.unlock();
}
}
// 读写锁(读多写少场景)
private Map<String, Object> cache = new HashMap<>();
public Object get(String key) {
rwLock.readLock().lock();
try {
return cache.get(key);
} finally {
rwLock.readLock().unlock();
}
}
public void put(String key, Object value) {
rwLock.writeLock().lock();
try {
cache.put(key, value);
} finally {
rwLock.writeLock().unlock();
}
}
// StampedLock(乐观读)
public double compute() {
long stamp = stampedLock.tryOptimisticRead();
double currentValue = balance * 1.0;
if (!stampedLock.validate(stamp)) {
stamp = stampedLock.readLock();
try {
currentValue = balance * 1.0;
} finally {
stampedLock.unlockRead(stamp);
}
}
return currentValue;
}
}
-
并发集合的使用
java
import java.util.concurrent.*;public class ConcurrentCollectionExample {
// 并发Map
private final ConcurrentHashMap<String, Object> cache =
new ConcurrentHashMap<>();// 并发队列 private final BlockingQueue<Request> requestQueue = new LinkedBlockingQueue<>(); // 线程安全的List private final CopyOnWriteArrayList<String> logList = new CopyOnWriteArrayList<>(); // 线程安全的Set private final ConcurrentSkipListSet<Integer> idSet = new ConcurrentSkipListSet<>(); // 线程安全的引用队列 private final ConcurrentLinkedQueue<EventListener> listeners = new ConcurrentLinkedQueue<>(); public void addToCache(String key, Object value) { cache.put(key, value); // 线程安全 } public Object computeIfAbsent(String key) { return cache.computeIfAbsent(key, k -> { // 线程安全地计算新值 return expensiveOperation(k); }); }}
-
初始化安全模式
Holder 模式
javapublic class LazyHolder {
// 静态内部类Holder模式(线程安全且懒加载)
private static class ConfigHolder {
static final Config INSTANCE = new Config();
}public static Config getInstance() { return ConfigHolder.INSTANCE; }}
枚举单例
java
public enum Singleton {
INSTANCE;private final AtomicInteger counter = new AtomicInteger(); public int getNext() { return counter.incrementAndGet(); }}
-
属性初始化策略
java
public class InitializationStrategies {
// 方案1:直接初始化(线程安全,类加载时初始化)
private final List<String> constantList = List.of("A", "B", "C");// 方案2:双重检查锁定(DCL) private volatile ExpensiveObject expensiveObject; public ExpensiveObject getExpensiveObject() { ExpensiveObject result = expensiveObject; if (result == null) { synchronized (this) { result = expensiveObject; if (result == null) { result = new ExpensiveObject(); expensiveObject = result; } } } return result; } // 方案3:初始化On-Demand Holder private static class Holder { static final InitializationStrategies INSTANCE = new InitializationStrategies(); } public static InitializationStrategies getInstance() { return Holder.INSTANCE; } // 方案4:使用VarHandle(JDK9+) private static final VarHandle COUNTER_HANDLE; static { try { COUNTER_HANDLE = MethodHandles.lookup() .findVarHandle(InitializationStrategies.class, "counter", int.class); } catch (Exception e) { throw new Error(e); } } private volatile int counter; public void increment() { COUNTER_HANDLE.getAndAdd(this, 1); }}
-
线程安全的Builder模式
java
public class ThreadSafeConfig {
private final String host;
private final int port;
private final boolean ssl;
private final int timeout;private ThreadSafeConfig(Builder builder) { this.host = builder.host; this.port = builder.port; this.ssl = builder.ssl; this.timeout = builder.timeout; } // 静态Builder类 public static class Builder { private String host = "localhost"; private int port = 8080; private boolean ssl = false; private int timeout = 5000; public Builder host(String host) { this.host = host; return this; } public Builder port(int port) { this.port = port; return this; } public Builder ssl(boolean ssl) { this.ssl = ssl; return this; } public Builder timeout(int timeout) { this.timeout = timeout; return this; } public ThreadSafeConfig build() { return new ThreadSafeConfig(this); } }}
// 使用示例
ThreadSafeConfig config = new ThreadSafeConfig.Builder()
.host("api.example.com")
.port(443)
.ssl(true)
.timeout(10000)
.build(); -
Spring框架中的线程安全
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
import java.util.concurrent.ConcurrentHashMap;@Component
@Scope("singleton") // 默认就是singleton
public class SpringService {// 注入的依赖通常是线程安全的(如果它们是单例且无状态) @Autowired private Repository repository; // 有状态的需要线程安全保护 private final ConcurrentHashMap<Long, Session> activeSessions = new ConcurrentHashMap<>(); // ThreadLocal用于线程隔离 private static final ThreadLocal<UserContext> userContext = ThreadLocal.withInitial(UserContext::new); // @PostConstruct初始化 @PostConstruct public void init() { // 线程安全的初始化 activeSessions.putIfAbsent(0L, Session.systemSession()); } @PreDestroy public void destroy() { userContext.remove(); // 清理ThreadLocal,防止内存泄漏 } // 使用Spring的@Scope("request")或@Scope("session") @Component @Scope(value = WebApplicationContext.SCOPE_REQUEST, proxyMode = ScopedProxyMode.TARGET_CLASS) public static class RequestScopedBean { // 每个请求一个实例,线程安全 }}
-
函数式编程风格
java
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.UnaryOperator;public class FunctionalStyle {
private final AtomicReference<State> state =
new AtomicReference<>(State.INITIAL);// 无副作用的更新 public void updateState(UnaryOperator<State> updateFunction) { State current, next; do { current = state.get(); next = updateFunction.apply(current); } while (!state.compareAndSet(current, next)); } // 使用不可变状态 public static class State { public static final State INITIAL = new State(0, "initial"); private final int version; private final String status; public State(int version, String status) { this.version = version; this.status = status; } public State next(String newStatus) { return new State(version + 1, newStatus); } }}
-
性能优化策略
javapublic class PerformanceOptimized {
// 1. 避免锁的优化
private final ThreadLocal<Counter> threadLocalCounter =
ThreadLocal.withInitial(Counter::new);// 2. 分段锁 private static final int STRIPES = 16; private final Object[] locks = new Object[STRIPES]; private final Map<String, String>[] segments = new HashMap[STRIPES]; public PerformanceOptimized() { for (int i = 0; i < STRIPES; i++) { locks[i] = new Object(); segments[i] = new HashMap<>(); } } public void put(String key, String value) { int stripe = Math.abs(key.hashCode()) % STRIPES; synchronized (locks[stripe]) { segments[stripe].put(key, value); } } // 3. 使用 LongAdder 代替 AtomicLong(高并发计数) private final LongAdder requestCount = new LongAdder(); private final LongAdder totalBytes = new LongAdder(); public void recordRequest(int bytes) { requestCount.increment(); totalBytes.add(bytes); }}
-
最佳实践总结表
场景 推荐方案 注意事项
只读配置 final + 构造器初始化 最简单的线程安全
计数器 AtomicInteger/LongAdder 高并发用LongAdder
共享对象引用 AtomicReference 配合CAS操作
缓存/Map ConcurrentHashMap computeIfAbsent线程安全
读多写少 ReadWriteLock/StampedLock 注意锁升级问题
懒加载单例 Holder模式 比DCL更简洁
线程局部存储 ThreadLocal 必须记得清理
对象池 ConcurrentLinkedQueue 无界队列注意内存
状态机 AtomicReference + 不可变状态 函数式风格
Spring Bean 单例+无状态或有状态用并发集合 避免实例变量 -
完整的实战示例
java
import java.util.concurrent.;
import java.util.concurrent.atomic.;
import java.util.function.*;/**
-
线程安全配置管理器的完整示例
*/
public class ThreadSafeConfigManager {// 使用volatile保证配置的可见性
private volatile Config currentConfig;// 原子引用保证配置引用的原子性
private final AtomicReference<Config> configRef =
new AtomicReference<>();// 并发Map存储不同环境的配置
private final ConcurrentHashMap<String, Config> configCache =
new ConcurrentHashMap<>();// 读写锁保护配置变更
private final ReadWriteLock configLock = new ReentrantReadWriteLock();// 用于懒加载的配置
private static class ConfigHolder {
static final ThreadSafeConfigManager INSTANCE =
new ThreadSafeConfigManager();
}private ThreadSafeConfigManager() {
// 初始化配置
this.currentConfig = loadDefaultConfig();
this.configRef.set(currentConfig);
}public static ThreadSafeConfigManager getInstance() {
return ConfigHolder.INSTANCE;
}/**
-
线程安全的配置更新(原子操作)
*/
public boolean updateConfig(Config newConfig) {
Config oldConfig = configRef.get();// CAS更新,保证原子性
while (!configRef.compareAndSet(oldConfig, newConfig)) {
oldConfig = configRef.get();
}// 更新volatile变量(保证可见性)
currentConfig = newConfig;return true;
}
/**
- 线程安全的配置获取
*/
public Config getConfig() {
// 直接读取volatile变量,保证可见性
return currentConfig;
}
/**
- 线程安全的延迟加载配置
*/
public Config getConfigForEnv(String env) {
return configCache.computeIfAbsent(env, key -> {
// 线程安全地计算新值
return loadConfigForEnv(key);
});
}
/**
- 使用读写锁保护复杂操作
*/
public void batchUpdate(Consumer<Config> updateFunction) {
configLock.writeLock().lock();
try {
Config config = configRef.get();
updateFunction.accept(config);
configRef.set(config);
currentConfig = config;
} finally {
configLock.writeLock().unlock();
}
}
/**
- 安全的配置复制(防御性复制)
*/
public Config getConfigCopy() {
configLock.readLock().lock();
try {
return currentConfig.deepCopy(); // 返回副本而不是引用
} finally {
configLock.readLock().unlock();
}
}
private Config loadDefaultConfig() {
return new Config();
}private Config loadConfigForEnv(String env) {
// 模拟加载配置
return new Config();
}// 不可变的配置类
public static final class Config {
private final String host;
private final int port;
private final List<String> servers;public Config() { this.host = "localhost"; this.port = 8080; this.servers = List.of("server1", "server2"); } public Config deepCopy() { return new Config(); }}
} -
-
-
测试线程安全性
java
import org.junit.jupiter.api.Test;
import java.util.concurrent.*;public class ThreadSafeTest {
@Test void testConcurrentUpdates() throws Exception { ThreadSafeConfigManager manager = ThreadSafeConfigManager.getInstance(); int threadCount = 100; ExecutorService executor = Executors.newFixedThreadPool(threadCount); CountDownLatch latch = new CountDownLatch(threadCount); AtomicInteger successCount = new AtomicInteger(); for (int i = 0; i < threadCount; i++) { final int id = i; executor.submit(() -> { try { Config newConfig = new Config(); if (manager.updateConfig(newConfig)) { successCount.incrementAndGet(); } } finally { latch.countDown(); } }); } latch.await(); executor.shutdown(); // 验证只有一个线程成功更新 assert successCount.get() == 1; }}
选择哪种方案取决于具体场景:
简单值更新:用 volatile 或原子类
复杂对象:用 AtomicReference + 不可变对象
集合操作:用并发集合
读多写少:用读写锁
性能要求高:用 StampedLock 或无锁算法
Spring环境:结合Spring作用域和并发集合
为什么需要设置为 final
- 不同的线程安全维度
java
// 两个不同的线程安全维度:
// 1. 引用本身的线程安全(final 保证)
// 2. 引用指向对象内容的线程安全(AtomicReference 保证)
// ❌ 错误:引用可能被修改,即使内容操作是原子的
private AtomicReference userRef = new AtomicReference<>();
// ✅ 正确:引用不可变 + 内容操作原子
private final AtomicReference userRef = new AtomicReference<>();
- final 的作用
java
public class Example {
// ❌ 引用可能被重新赋值(线程不安全)
private AtomicReference userRef = new AtomicReference<>();
public void resetReference() {
// 这里可以重新赋值,导致其他线程看到不一致的引用
userRef = new AtomicReference<>(newUser);
// 问题:不同线程可能看到不同的 userRef 实例!
}
// ✅ final 保证引用不变
private final AtomicReference<User> finalUserRef = new AtomicReference<>();
public void cannotResetReference() {
// finalUserRef = new AtomicReference<>(); // 编译错误!
// 所有线程都使用同一个 AtomicReference 实例
}
}
- 内存可见性保证
final 提供额外的安全发布保证:
java
public class SafePublication {
// final 确保构造完成后,引用对所有线程可见
private final AtomicReference configRef;
public SafePublication(Config initialConfig) {
this.configRef = new AtomicReference<>(initialConfig);
// 由于 final,其他线程看到 configRef 时,它已经正确初始化
}
// 如果没有 final,可能发生指令重排导致可见性问题
}
- 实际场景对比
场景A:需要 final 的情况
java
// 计数器类 - 引用不应该改变
public class CounterService {
// ✅ 正确:final 确保所有线程操作同一个计数器
private final AtomicInteger requestCount = new AtomicInteger(0);
private final AtomicLong totalBytes = new AtomicLong(0);
public void recordRequest(int bytes) {
requestCount.incrementAndGet();
totalBytes.addAndGet(bytes);
// 所有线程都在同一个计数器上累加
}
}
场景B:可能不需要 final 的情况
java
// 缓存管理器 - 可能需要替换整个缓存实例
public class CacheManager {
// ❌ 可能需要更换缓存实现
private final AtomicReference cache = new AtomicReference<>();
// 更好的设计:使用 volatile 或更好的策略
private volatile Cache cache = new ConcurrentCache();
public void switchCache(Cache newCache) {
// 允许替换整个缓存实例
this.cache = newCache;
}
}
- 最佳实践模式
模式1:final + 原子类(推荐)
java
public class Configuration {
// final 保证引用安全,AtomicReference 保证内容安全
private final AtomicReference configRef;
private final AtomicInteger version = new AtomicInteger(0);
public Configuration(Config initialConfig) {
this.configRef = new AtomicReference<>(initialConfig);
}
public void updateConfig(Config newConfig) {
configRef.set(newConfig);
version.incrementAndGet();
}
// 无法改变引用:configRef = new AtomicReference<>(); // 编译错误
}
模式2:工厂方法创建不可变对象
java
public class UserManager {
// final + 原子引用,完全线程安全
private final AtomicReference currentUser = new AtomicReference<>();
// 通过工厂方法创建不可变用户
public void login(String username, String password) {
User newUser = User.createImmutable(username, password);
currentUser.set(newUser); // 原子操作
}
// 内部静态类,不可变用户
public static final class User {
private final String username;
private final String hashedPassword;
private User(String username, String hashedPassword) {
this.username = username;
this.hashedPassword = hashedPassword;
}
public static User createImmutable(String username, String password) {
String hashed = hashPassword(password);
return new User(username, hashed);
}
}
}
- 性能考量
java
public class PerformanceExample {
// final 可能带来性能优化
private final AtomicInteger counter = new AtomicInteger(0);
// 1. JIT 优化:final 字段更容易被优化
// 2. 内存屏障减少:final 有更简单的内存语义
// 3. 内联可能性:final 字段更容易被内联
// 对比:非 final 需要额外的内存屏障
private AtomicInteger nonFinalCounter = new AtomicInteger(0);
}
- 完整的线程安全设计
java
import java.util.concurrent.atomic.*;
import java.util.function.UnaryOperator;
/**
-
完整的线程安全组件示例
*/
public class ThreadSafeComponent {
// 1. final + 原子类(基础类型)
private final AtomicInteger requestId = new AtomicInteger(0);
private final AtomicLong lastUpdated = new AtomicLong(System.currentTimeMillis());
// 2. final + 原子引用(对象引用)
private final AtomicReference state = new AtomicReference<>(State.IDLE);
private final AtomicReference currentConnection = new AtomicReference<>();
// 3. final + 不可变配置
private final ImmutableConfig config;
// 4. 如果需要替换整个容器,使用 volatile
private volatile Cache cache = new DefaultCache();
public ThreadSafeComponent(ImmutableConfig config) {
this.config = config; // final 确保安全发布
}
/**
- 线程安全的ID生成
*/
public int nextRequestId() {
// final 确保所有线程使用同一个 AtomicInteger
return requestId.incrementAndGet();
}
/**
-
线程安全的状态转换
*/
public boolean transitionTo(State newState) {
State current;
do {
current = state.get();
if (!current.canTransitionTo(newState)) {
return false;
}
} while (!state.compareAndSet(current, newState));
lastUpdated.set(System.currentTimeMillis());
return true;
}
/**
- 复杂的原子更新
*/
public void updateConnection(UnaryOperator update) {
Connection current, next;
do {
current = currentConnection.get();
next = update.apply(current != null ? current.clone() : null);
} while (!currentConnection.compareAndSet(current, next));
}
/**
- 允许替换整个缓存实例
*/
public void upgradeCache(Cache newCache) {
// volatile 确保可见性
this.cache = newCache;
}
// 枚举状态,线程安全
public enum State {
IDLE, CONNECTING, CONNECTED, DISCONNECTING;
boolean canTransitionTo(State newState) { // 状态转换逻辑 return true; }}
// 不可变配置类
public static final class ImmutableConfig {
private final String host;
private final int port;
public ImmutableConfig(String host, int port) { this.host = host; this.port = port; }}
}
- 线程安全的ID生成
- 测试验证
java
import org.junit.jupiter.api.Test;
import java.util.concurrent.*;
import static org.junit.jupiter.api.Assertions.*;
class ThreadSafeComponentTest {
@Test
void testFinalAtomicReference() throws Exception {
ThreadSafeComponent component = new ThreadSafeComponent(
new ThreadSafeComponent.ImmutableConfig("localhost", 8080)
);
// 测试多个线程同时操作
int threadCount = 100;
ExecutorService executor = Executors.newFixedThreadPool(threadCount);
CountDownLatch latch = new CountDownLatch(threadCount);
// 收集所有生成的ID
ConcurrentSkipListSet<Integer> ids = new ConcurrentSkipListSet<>();
for (int i = 0; i < threadCount; i++) {
executor.submit(() -> {
int id = component.nextRequestId();
ids.add(id);
latch.countDown();
});
}
latch.await();
executor.shutdown();
// 验证:没有重复的ID
assertEquals(threadCount, ids.size());
// 验证:ID从1开始连续
assertEquals(1, ids.first());
assertEquals(threadCount, ids.last());
}
@Test
void testReferenceSafety() {
// 创建组件
ThreadSafeComponent component = new ThreadSafeComponent(
new ThreadSafeComponent.ImmutableConfig("test", 1234)
);
// 使用反射尝试修改 final 字段(应该失败或抛出异常)
try {
// 这会抛出异常,因为字段是 final
// java.lang.IllegalAccessException: Can not set static final field
} catch (Exception e) {
// 期望的行为
}
}
}
- 什么时候不使用 final
只有在你确实需要替换整个原子类实例时才不使用 final:
java
public class DynamicCacheManager {
// 需要动态更换实现
private AtomicReference strategy = new AtomicReference<>();
public void switchStrategy(CacheStrategy newStrategy) {
// 允许替换整个策略对象
strategy.set(newStrategy);
// 但更好的设计可能是:
// 1. 使用 volatile
// 2. 或使用 AtomicReference 但通过方法控制
}
// 更好的替代方案
private volatile CacheStrategy volatileStrategy = new DefaultStrategy();
public void betterSwitch(CacheStrategy newStrategy) {
volatileStrategy = newStrategy; // volatile 保证可见性
}
}
- 总结规则
情况 是否用 final 原因
计数器、状态等 ✅ 必须用 确保所有线程操作同一个实例
配置、上下文等 ✅ 推荐用 安全发布,避免引用变更
可能需要热替换 ⚠️ 谨慎用 考虑使用 volatile 替代
Spring Bean 注入 ✅ 应该用 配合 @Autowired 或构造器注入
核心原则:
默认加上 final:除非有明确理由不要
原子类保证内容安全:AtomicReference 保证引用指向的对象内容操作是原子的
final 保证引用安全:final 保证引用本身不被重新赋值
组合使用最安全:final + AtomicReference = 引用不可变 + 内容操作原子
记忆口诀:
"原子保内容,final保引用;
两者结合用,线程最安心。"
所以,回到你的问题:AtomicReference 已经是原子性操作了,还需要设置为 final 吗?
答案是:需要! 因为 AtomicReference 保证的是引用指向的对象内容的原子操作,而 final 保证的是引用本身不被重新赋值。它们是互补的,不是重复的。
#专项锁和 this锁使用
-
关键区别对比
特性 专用锁对象 private final Object lock this 锁 推荐度
封装性 ✅ 高(外部不可访问) ❌ 低(外部可访问) 专用锁优
安全性 ✅ 高(避免外部死锁) ⚠️ 中(可能外部同步) 专用锁优
灵活性 ✅ 高(可创建多个锁) ❌ 低(整个对象一把锁) 专用锁优
性能 ⚠️ 稍优(锁竞争少) ⚠️ 稍差(竞争可能多) 专用锁优
代码清晰度 ✅ 高(明确锁用途) ⚠️ 中(锁用途不明确) 专用锁优
-
为什么推荐专用锁对象
2.1 封装性优势
java
public class BankAccount {
// ✅ 专用锁:外部无法获取,避免外部同步导致死锁
private final Object balanceLock = new Object();
private double balance;
public void transfer(BankAccount to, double amount) {
// 正确:使用专用锁,外部无法干扰
synchronized (balanceLock) {
this.balance -= amount;
}
}
// 外部代码无法这样做:
// synchronized (account.balanceLock) { ... } // 编译错误,private!
}
// ❌ 使用 this 锁的问题
public class ProblematicAccount {
private double balance;
public synchronized void transfer(ProblematicAccount to, double amount) {
// 可能发生死锁:外部代码可能也同步这个对象
}
// 外部代码可能这样做,导致意外死锁:
// synchronized (account) {
// // 做一些其他事情
// }
}
2.2 细粒度锁控制
java
public class DatabaseService {
// ✅ 多个专用锁实现细粒度控制
private final Object connectionLock = new Object();
private final Object queryLock = new Object();
private final Object cacheLock = new Object();
private Connection connection;
private List<Query> activeQueries;
private Map<String, Object> cache;
public void manageConnection() {
synchronized (connectionLock) {
// 只锁定连接相关操作
// 查询和缓存操作可以并发进行
}
}
public void executeQuery() {
synchronized (queryLock) {
// 只锁定查询操作
}
}
// ❌ 使用 this 锁:所有操作互斥,并发性差
public synchronized void doEverything() {
// 连接、查询、缓存操作全部串行化
}
}
- 可以使用 this 的场景
3.1 简单的单例或工具类
java
public class SimpleCache {
// 简单场景:只有一个Map需要保护
private final Map<String, Object> cache = new HashMap<>();
// ✅ 可以使用 this,因为:
// 1. 类很简单,只有一种资源需要保护
// 2. 通常不会有外部同步这个对象
public synchronized void put(String key, Object value) {
cache.put(key, value);
}
public synchronized Object get(String key) {
return cache.get(key);
}
}
3.2 小型内部类或辅助类
java
public class OuterClass {
private List dataList = new ArrayList<>();
// 内部类,简单同步
private class DataProcessor {
// ✅ 可以使用 this,因为:
// 1. 内部类,范围小
// 2. 外部通常不会同步这个内部类实例
public synchronized void process() {
// 处理数据
}
}
}
3.3 明确设计为全局锁的类
java
// 设计意图:整个对象作为一个全局锁
public class GlobalLock {
// 明确文档说明:这个对象本身作为锁
private final Object sharedResource;
// ✅ 设计为使用 this 作为锁
public synchronized void exclusiveOperation() {
// 需要全局互斥的操作
}
// 在文档中明确说明:
/**
* 警告:这个类的实例本身作为锁。
* 不要在其他地方同步这个对象,否则可能导致死锁。
*/
}
- 具体场景分析
场景1:账户转账(专用锁更安全)
java
public class BankAccount {
// 方案A:专用锁(推荐)
private final Object lock = new Object();
private BigDecimal balance;
// 方案B:this锁(不推荐)
// private BigDecimal balance;
public void transferTo(BankAccount target, BigDecimal amount) {
// ✅ 正确:使用专用锁,避免死锁
synchronized (lock) {
this.balance = this.balance.subtract(amount);
// 注意:这里应该使用锁排序避免死锁
}
// ❌ 危险:如果两个账户都使用this锁,可能死锁
// synchronized (this) {
// synchronized (target) { // 容易死锁
// this.balance = this.balance.subtract(amount);
// target.balance = target.balance.add(amount);
// }
// }
}
}
场景2:连接池管理(专用锁更高效)
java
public class ConnectionPool {
// ✅ 专用锁:不同资源使用不同锁
private final Object availableConnectionsLock = new Object();
private final Object inUseConnectionsLock = new Object();
private final Object statisticsLock = new Object();
private List<Connection> availableConnections;
private Set<Connection> inUseConnections;
private Statistics stats;
public Connection getConnection() {
Connection conn;
// 只锁可用连接列表
synchronized (availableConnectionsLock) {
conn = availableConnections.remove(0);
}
// 更新使用中连接集合(不影响其他操作)
synchronized (inUseConnectionsLock) {
inUseConnections.add(conn);
}
// 更新统计(独立锁)
synchronized (statisticsLock) {
stats.incrementAcquisitions();
}
return conn;
}
// ❌ 使用this锁:所有操作串行化,性能差
// public synchronized Connection getConnection() {
// // 获取连接、更新集合、更新统计全部互斥
// }
}
- 性能影响分析
java
public class PerformanceComparison {
// 测试两种方式的性能差异
// 方式1:专用锁
private final Object lock1 = new Object();
private final Object lock2 = new Object();
private int counter1 = 0;
private int counter2 = 0;
// 方式2:this锁
private int counter3 = 0;
private int counter4 = 0;
public void incrementWithSeparateLocks() {
// 两个计数器可以并行递增
synchronized (lock1) {
counter1++;
}
synchronized (lock2) {
counter2++;
}
}
public synchronized void incrementWithThisLock() {
// 两个计数器串行递增
counter3++;
counter4++;
}
// 在多线程环境下,专用锁的性能通常更好
}
- 死锁风险对比
java
public class DeadlockExample {
// 场景:两个账户相互转账
static class AccountWithThisLock {
private BigDecimal balance;
public synchronized void transfer(AccountWithThisLock target, BigDecimal amount) {
// ❌ 危险:容易死锁
// 线程1: A.transfer(B)
// 线程2: B.transfer(A)
// 可能发生死锁:线程1持有A锁,等待B锁;线程2持有B锁,等待A锁
synchronized (target) {
this.balance = this.balance.subtract(amount);
target.balance = target.balance.add(amount);
}
}
}
static class AccountWithPrivateLock {
private final Object lock = new Object();
private BigDecimal balance;
public void transfer(AccountWithPrivateLock target, BigDecimal amount) {
// ✅ 仍然可能死锁,但更容易控制
// 可以使用锁排序避免死锁
Object firstLock = System.identityHashCode(this) < System.identityHashCode(target)
? this.lock : target.lock;
Object secondLock = System.identityHashCode(this) < System.identityHashCode(target)
? target.lock : this.lock;
synchronized (firstLock) {
synchronized (secondLock) {
this.balance = this.balance.subtract(amount);
target.balance = target.balance.add(amount);
}
}
}
}
}
- 最佳实践总结
使用专用锁的情况:
java
public class BestPracticeExample {
// ✅ 当类有多个独立资源需要保护时
private final Object resource1Lock = new Object();
private final Object resource2Lock = new Object();
// ✅ 当类可能被外部代码使用时
private final Object internalLock = new Object();
// ✅ 当需要避免与父类/子类锁冲突时
private final Object subclassSafeLock = new Object();
// ✅ 高性能要求的并发数据结构
private final StripedLock stripedLock = new StripedLock(16);
}
可以使用 this 锁的情况:
java
public class AcceptableThisLock {
// ⚠️ 简单的值对象或DTO
private int value;
// ⚠️ 私有内部类或辅助类
private static class Helper {
private int count;
public synchronized void increment() { count++; }
}
// ⚠️ 明确设计为全局互斥的类
// (但最好还是用专用锁并明确文档说明)
}
- 重构示例
从 this 锁重构为专用锁
java
// 重构前:使用 this 锁
public class LegacyService {
private Map<String, Data> cache = new HashMap<>();
private int hitCount;
private int missCount;
public synchronized Data get(String key) {
Data data = cache.get(key);
if (data != null) {
hitCount++;
} else {
missCount++;
}
return data;
}
public synchronized void put(String key, Data data) {
cache.put(key, data);
}
}
// 重构后:使用专用锁(性能更好)
public class RefactoredService {
private final Map<String, Data> cache = new HashMap<>();
private final AtomicInteger hitCount = new AtomicInteger();
private final AtomicInteger missCount = new AtomicInteger();
private final Object cacheLock = new Object(); // 只保护缓存
public Data get(String key) {
Data data;
synchronized (cacheLock) {
data = cache.get(key);
}
// 计数器使用原子操作,不需要锁
if (data != null) {
hitCount.incrementAndGet();
} else {
missCount.incrementAndGet();
}
return data;
}
public void put(String key, Data data) {
synchronized (cacheLock) {
cache.put(key, data);
}
}
}
- 特殊情况考虑
继承中的锁问题
java
public class BaseClass {
// ❌ 使用 this 锁:子类可能重写同步方法,造成意外
public synchronized void doSomething() {
// ...
}
}
public class DerivedClass extends BaseClass {
// 重写父类方法,可能破坏同步语义
@Override
public synchronized void doSomething() {
super.doSomething(); // 仍然使用 this 锁
// 可能与其他方法产生意外的锁竞争
}
public synchronized void otherMethod() {
// 使用同一个锁(this),可能与父类方法互斥
}
}
// 解决方案:使用专用锁
public class BetterBaseClass {
protected final Object operationLock = new Object();
public void doSomething() {
synchronized (operationLock) {
// ...
}
}
}
与现有API的兼容性
java
public class LegacyCompatibleClass {
// 需要与使用 this 锁的遗留代码兼容
private final Object internalLock = new Object();
// 对外提供同步方法(使用 this 锁保持兼容)
public synchronized void legacyCompatibleMethod() {
// 但内部实现使用专用锁
synchronized (internalLock) {
// 实际实现
}
}
// 新方法使用专用锁
public void newMethod() {
synchronized (internalLock) {
// ...
}
}
}
-
决策流程
-
总结建议
黄金规则:
默认使用专用锁对象:private final Object lock = new Object();
避免使用 this 作为锁,除非有明确的理由
考虑锁的粒度:细粒度锁通常性能更好
文档记录锁策略:如果使用不寻常的锁策略,务必文档化
实际建议:
java
// ✅ 推荐:始终使用专用锁
public class RecommendedClass {
// 清晰表明这个锁用于保护什么
private final Object dataStructureLock = new Object();
private final Object ioOperationLock = new Object();
// 如果需要,可以创建更多专用锁
// private final Object cacheLock = new Object();
// private final Object connectionLock = new Object();
}
// ❌ 不推荐:使用 this 锁(除非有充分理由)
public class NotRecommendedClass {
public synchronized void method1() { /* 与其他同步方法互斥 / }
public synchronized void method2() { / 可能与method1不必要互斥 */ }
}
最终答案:
通常不应该用 this 替换专用锁对象。 专用锁对象提供更好的封装性、安全性和灵活性。只有在非常简单的、内部使用的、确定不会有外部同步或继承问题的小型类中,才可以考虑使用 this 锁。在绝大多数情况下,专用锁是更安全、更灵活的选择。
🔗 完整死锁场景代码
下面的例子模拟了两个账户相互转账,同时一个"外部审计系统"也在同步检查账户的场景。
java
class ProblematicAccount {
private double balance;
private final String name;
public ProblematicAccount(String name, double balance) {
this.name = name;
this.balance = balance;
}
// ❌ 危险:使用 this 作为内部锁
public synchronized void transfer(ProblematicAccount target, double amount) {
System.out.println(Thread.currentThread().getName() + ": 尝试获取 [" + this.name + "] 的锁...");
// 第一步:当前线程已经持有 this(源账户)的锁
// 模拟一些操作,增加死锁发生窗口
try { Thread.sleep(10); } catch (InterruptedException e) {}
System.out.println(Thread.currentThread().getName() + ": 持有 [" + this.name + "] 的锁,尝试获取 [" + target.name + "] 的锁...");
// ❌ 第二步:尝试获取 target(目标账户)的锁
// 这里隐式地使用了 synchronized(target),等待 target 对象的监视器锁
synchronized(target) {
System.out.println(Thread.currentThread().getName() + ": 成功获取 [" + target.name + "] 的锁,开始转账。");
if (this.balance >= amount) {
this.balance -= amount;
target.balance += amount;
System.out.println(Thread.currentThread().getName() + ": 从 " + this.name + " 向 " + target.name + " 转账 " + amount + " 完成。");
}
}
}
public synchronized void audit() {
System.out.println(Thread.currentThread().getName() + ": 审计系统正在检查账户 [" + this.name + "],余额为:" + this.balance);
// 模拟审计耗时
try { Thread.sleep(50); } catch (InterruptedException e) {}
}
public String getName() { return name; }
}
public class DeadlockDemo {
public static void main(String[] args) throws InterruptedException {
ProblematicAccount accountA = new ProblematicAccount("账户A", 1000);
ProblematicAccount accountB = new ProblematicAccount("账户B", 1000);
// 线程1:A 向 B 转账
Thread t1 = new Thread(() -> {
accountA.transfer(accountB, 100);
}, "转账线程-AtoB");
// 线程2:B 向 A 转账
Thread t2 = new Thread(() -> {
accountB.transfer(accountA, 200);
}, "转账线程-BtoA");
// 线程3:外部审计系统(例如一个定时任务),它可能在任何时候锁定任何一个账户
Thread t3 = new Thread(() -> {
System.out.println("外部审计线程启动,即将锁定账户A进行深度检查...");
// 外部代码直接使用了 accountA 对象作为锁!
synchronized (accountA) {
System.out.println("外部审计线程:已锁定账户A,开始深度审计...");
try {
Thread.sleep(1000); // 模拟一个长时间的审计操作
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("外部审计线程:对账户A的深度审计完成。");
}
}, "外部审计线程");
// 启动线程,故意让审计线程先启动一点点,让它先锁住账户A
t3.start();
Thread.sleep(50); // 确保t3先拿到 accountA 的锁
// 然后启动两个转账线程
t1.start();
t2.start();
// 等待所有线程结束(实际上会因为死锁永远等不到)
t1.join(2000);
t2.join(100);
t3.join(100);
System.out.println("\n===== 程序可能卡死在上方,以下是死锁分析 =====");
System.out.println("如果程序没有继续,说明发生了死锁。锁的持有关系如下:");
System.out.println("1. 【外部审计线程】持有了【账户A】的锁,并在进行长时间操作。");
System.out.println("2. 【转账线程-AtoB】在等待【外部审计线程】释放【账户A】的锁。");
System.out.println("3. 【转账线程-BtoA】持有了【账户B】的锁,并在等待【转账线程-AtoB】释放【账户B】的锁。");
System.out.println("4. 【外部审计线程】完成后,会释放【账户A】的锁,但此时【转账线程-BtoA】还在等待,而【转账线程-AtoB】永远等不到【账户A】的锁。");
System.out.println("💡 根本原因:`ProblematicAccount` 的锁(`this`)是公开的,无法防止外部代码(审计线程)干扰其内部的锁获取顺序。");
}
}
📊 死锁发生过程分析
运行上述代码,很可能出现程序卡住的情况。下面表格展示了死锁发生的典型时间线:
时间顺序 转账线程-AtoB (t1) 转账线程-BtoA (t2) 外部审计线程 (t3) 锁的持有情况
初始 等待启动 等待启动 启动并运行 t3 持有 accountA 锁
t1启动 尝试执行 accountA.transfer(...),
需要获取 accountA 锁,但被 t3 阻塞 等待启动 持有 accountA 锁,慢速审计中 t3 持有 accountA
t2启动 阻塞中... 尝试执行 accountB.transfer(...),
成功获取 accountB 锁,然后尝试获取 accountA 锁(被t3阻塞) 审计中... t2 持有 accountB, t3 持有 accountA
僵局形成 等待 accountA (被t3持有) 持有 accountB,等待 accountA (被t3持有) 持有 accountA,完成后自然释放 循环等待链形成
死锁 即使t3释放了accountA,t1获得它后,又会去请求accountB(此时被t2持有)。而t2在等待t1释放accountA。结果:t1等t2放B,t2等t1放A,经典死锁。
💡 如何避免:使用专用私有锁
解决方案就是最初建议的:使用一个对外不可见的私有锁对象。
java
class SafeAccount {
private double balance;
private final String name;
// ✅ 解决方案:专用私有锁对象
private final Object lock = new Object();
public SafeAccount(String name, double balance) { /*...*/ }
public void transfer(SafeAccount target, double amount) {
// 关键:使用私有锁进行排序,外部代码无法干扰
Object firstLock = System.identityHashCode(this) < System.identityHashCode(target) ? this.lock : target.lock;
Object secondLock = System.identityHashCode(this) < System.identityHashCode(target) ? target.lock : this.lock;
synchronized (firstLock) {
synchronized (secondLock) {
if (this.balance >= amount) {
this.balance -= amount;
target.balance += amount;
}
}
}
}
public void audit() {
// 审计也使用私有锁,与转账操作互斥,但外部无法干扰顺序
synchronized (lock) {
System.out.println("审计账户: " + name + ", 余额: " + balance);
}
}
}
总结:synchronized(this) 或使用实例本身作为锁,相当于将类的同步机制"公有化",破坏了封装性,使得该类的线程安全性变得脆弱且不可控。而私有锁对象 (private final Object lock) 将锁隐藏起来,是确保线程安全内部细节不被外界破坏的最佳实践。