最近项目中遇到如下的场景:在执行数据迁移时,需要按照用户粒度加锁,因此考虑使用排他锁,迁移工具和业务服务属于两个服务,因此需要使用分布式锁。
我们使用缓存(Tair或者Redis)实现分布式锁,具体代码如下:
代码语言:javascript复制@Service
public class Locker {
@Resource(name = "tairClientUtil")
private TairClientUtil tairClientUtil;
private ThreadLocal<Long> lockerBeanThreadLocal = new ThreadLocal<>();
public void init(long userid) {
lockerBeanThreadLocal.remove();
lockerBeanThreadLocal.set(userid);
}
public void updateLock() {
String lockKey = Constants.MIGRATION_PREFIX lockerBeanThreadLocal.get();
tairClientUtil.incr(lockKey, Constants.COUNT_EXPIRE);
}
public void invalidLock() {
String lockKey = Constants.MIGRATION_PREFIX lockerBeanThreadLocal.get();
tairClientUtil.invalid(lockKey);
}
}
因为每个线程可能携带不同的userid发起请求,因此在这里使用ThreadLocal变量存放userid,使得每个线程都有一份自己的副本。
参考官方文档:ThreadLocal的用法,这个类提供“thread-local”变量,这些变量与线程的局部变量不同,每个线程都保存一份改变量的副本,可以通过get或者set方法访问。如果开发者希望将类的某个静态变量(user ID或者transaction ID)与线程状态关联,则可以考虑使用ThreadLocal。
举个例子,下面的类为每个线程生成不同的ID,当某个线程第一次调用Thread.get()时,会为该线程赋予一个ID,并且在后续的调用中不再改变。
代码语言:javascript复制 import java.util.concurrent.atomic.AtomicInteger;
public class ThreadId {
// Atomic integer containing the next thread ID to be assigned
private static final AtomicInteger nextId = new AtomicInteger(0);
// Thread local variable containing each thread's ID
private static final ThreadLocal<Integer> threadId =
new ThreadLocal<Integer>() {
@Override protected Integer initialValue() {
return nextId.getAndIncrement();
}
};
// Returns the current thread's unique ID, assigning it if necessary
public static int get() {
return threadId.get();
}
}
每个线程会“隐式”包含一份thread-local变量的副本,只要线程还处于活跃状态,就可以访问该变量;当线程停止后,如果没有其他线程持有该thread-local变量,则该变量的副本会提交给垃圾回收器。