本文目录导读:

我来详细解释Java中实现版本控制锁的几种常见方式。
乐观锁实现(CAS方式)
使用版本号的简单实现
import java.util.concurrent.atomic.AtomicInteger;
// 数据实体类
class OptimisticData {
private String id;
private String content;
private volatile int version; // 版本号
public OptimisticData(String id, String content) {
this.id = id;
this.content = content;
this.version = 0;
}
// getters and setters
public String getId() { return id; }
public String getContent() { return content; }
public int getVersion() { return version; }
public void setContent(String content) { this.content = content; }
public void setVersion(int version) { this.version = version; }
}
// 乐观锁管理器
class OptimisticLockManager {
/**
* 更新数据(使用版本号检查)
*/
public boolean updateData(OptimisticData data, String newContent, int expectedVersion) {
// 模拟数据库操作
synchronized (this) {
// 检查版本号是否匹配
if (data.getVersion() == expectedVersion) {
data.setContent(newContent);
data.setVersion(expectedVersion + 1);
return true;
}
return false;
}
}
}
使用AtomicStampedReference防止ABA问题
import java.util.concurrent.atomic.AtomicStampedReference;
class VersionedResource {
private AtomicStampedReference<String> resource;
public VersionedResource(String initialValue) {
// 初始版本号为0
this.resource = new AtomicStampedReference<>(initialValue, 0);
}
/**
* 更新资源(包含版本号检查)
*/
public boolean update(String newValue, int expectedStamp) {
String currentValue = resource.getReference();
int currentStamp = resource.getStamp();
// 检查版本号是否匹配
if (currentStamp == expectedStamp) {
return resource.compareAndSet(currentValue, newValue, currentStamp, currentStamp + 1);
}
return false;
}
// 测试ABA问题的演示
public static void main(String[] args) {
VersionedResource resource = new VersionedResource("初始值");
// 正常更新
boolean result1 = resource.update("新值", 0);
System.out.println("更新结果1: " + result1); // true
// 使用过期版本号更新
boolean result2 = resource.update("另一个值", 0);
System.out.println("更新结果2: " + result2); // false
}
}
数据库级别的版本控制锁
import java.sql.*;
import java.util.Date;
class DatabaseVersionLock {
private static final String DB_URL = "jdbc:mysql://localhost:3306/test";
private static final String USER = "root";
private static final String PASS = "password";
/**
* 带版本控制的更新操作
*/
public boolean updateWithVersion(String id, String newData, int expectedVersion) {
String sql = "UPDATE data_table SET content = ?, version = version + 1, update_time = ? " +
"WHERE id = ? AND version = ?";
try (Connection conn = DriverManager.getConnection(DB_URL, USER, PASS);
PreparedStatement stmt = conn.prepareStatement(sql)) {
stmt.setString(1, newData);
stmt.setTimestamp(2, new Timestamp(new Date().getTime()));
stmt.setString(3, id);
stmt.setInt(4, expectedVersion);
int rowsAffected = stmt.executeUpdate();
return rowsAffected > 0; // 返回true表示更新成功
} catch (SQLException e) {
e.printStackTrace();
return false;
}
}
/**
* 创建数据表(示例)
*/
public static void createTable() {
String sql = "CREATE TABLE IF NOT EXISTS data_table (" +
"id VARCHAR(50) PRIMARY KEY, " +
"content TEXT, " +
"version INT DEFAULT 0, " +
"update_time TIMESTAMP" +
")";
try (Connection conn = DriverManager.getConnection(DB_URL, USER, PASS);
Statement stmt = conn.createStatement()) {
stmt.execute(sql);
} catch (SQLException e) {
e.printStackTrace();
}
}
}
分布式版本控制锁(使用Redis)
import redis.clients.jedis.Jedis;
import redis.clients.jedis.params.SetParams;
import java.util.UUID;
class RedisVersionLock {
private Jedis jedis;
public RedisVersionLock() {
this.jedis = new Jedis("localhost", 6379);
}
/**
* 获取分布式锁
*/
public String acquireLock(String lockKey, long timeoutSeconds) {
String lockValue = UUID.randomUUID().toString();
String result = jedis.set(lockKey, lockValue,
SetParams.setParams().nx().ex(timeoutSeconds));
if ("OK".equals(result)) {
return lockValue; // 返回锁的标识,用于释放锁
}
return null; // 获取锁失败
}
/**
* 释放分布式锁(使用Lua脚本保证原子性)
*/
public boolean releaseLock(String lockKey, String lockValue) {
String script = "if redis.call('get', KEYS[1]) == ARGV[1] then " +
"return redis.call('del', KEYS[1]) " +
"else return 0 end";
Object result = jedis.eval(script, 1, lockKey, lockValue);
return "1".equals(result.toString());
}
/**
* 带版本控制的更新操作
*/
public boolean updateWithVersionControl(String key, String newValue, int expectedVersion) {
String versionKey = key + ":version";
// 使用Lua脚本保证原子性
String script = "local current_version = redis.call('get', KEYS[2]) " +
"if current_version and tonumber(current_version) == tonumber(ARGV[2]) then " +
"redis.call('set', KEYS[1], ARGV[1]) " +
"redis.call('incr', KEYS[2]) " +
"return 1 " +
"else return 0 end";
Object result = jedis.eval(script, 2, key, versionKey, newValue,
String.valueOf(expectedVersion));
return "1".equals(result.toString());
}
}
完整的版本控制锁框架示例
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.locks.ReentrantLock;
class VersionLockFramework {
private final Map<String, VersionedResource> resources = new ConcurrentHashMap<>();
private final ReentrantLock lock = new ReentrantLock();
static class VersionedResource {
private Object data;
private int version;
private long lastModified;
public VersionedResource(Object data) {
this.data = data;
this.version = 0;
this.lastModified = System.currentTimeMillis();
}
public int getVersion() { return version; }
public Object getData() { return data; }
public long getLastModified() { return lastModified; }
}
/**
* 版本控制更新操作
*/
public boolean updateWithVersion(String resourceId, Object newData, int expectedVersion) {
lock.lock();
try {
VersionedResource resource = resources.get(resourceId);
if (resource == null) {
throw new IllegalArgumentException("Resource not found: " + resourceId);
}
// 检查版本号
if (resource.getVersion() != expectedVersion) {
return false; // 版本冲突
}
// 执行更新
resource.data = newData;
resource.version++;
resource.lastModified = System.currentTimeMillis();
return true;
} finally {
lock.unlock();
}
}
/**
* 创建资源
*/
public VersionedResource createResource(String resourceId, Object initialData) {
lock.lock();
try {
VersionedResource resource = new VersionedResource(initialData);
resources.put(resourceId, resource);
return resource;
} finally {
lock.unlock();
}
}
/**
* 获取当前版本号
*/
public int getCurrentVersion(String resourceId) {
VersionedResource resource = resources.get(resourceId);
return resource != null ? resource.getVersion() : -1;
}
}
使用示例和测试
public class VersionControlDemo {
public static void main(String[] args) throws InterruptedException {
// 测试基本版本控制
VersionLockFramework framework = new VersionLockFramework();
// 创建资源
framework.createResource("doc1", "原始内容");
int currentVersion = framework.getCurrentVersion("doc1");
System.out.println("初始版本号: " + currentVersion); // 0
// 成功更新
boolean success = framework.updateWithVersion("doc1", "修改内容1", currentVersion);
System.out.println("更新成功: " + success); // true
System.out.println("当前版本号: " + framework.getCurrentVersion("doc1")); // 1
// 尝试使用过期版本号更新(模拟并发冲突)
boolean failed = framework.updateWithVersion("doc1", "修改内容2", currentVersion);
System.out.println("使用过期版本号更新: " + failed); // false
// 正确的版本号更新
success = framework.updateWithVersion("doc1", "修改内容2", 1);
System.out.println("使用正确版本号更新: " + success); // true
}
}
版本控制锁的最佳实践
-
选择合适的实现方式:
- 单机应用:使用乐观锁或synchronized
- 分布式系统:使用Redis或ZooKeeper
-
注意ABA问题:
使用AtomicStampedReference或版本号递增
-
性能考虑:
- 乐观锁适合读多写少的场景
- 悲观锁适合写操作频繁的场景
-
错误处理:
- 版本冲突时提供明确的错误信息
- 实现重试机制
这些实现可以根据你的具体需求进行调整和组合使用。