最近專案中遇到如下的場景:在執行資料遷移時,需要按照使用者粒度加鎖,因此考慮使用排他鎖,遷移工具和業務服務屬於兩個服務,因此需要使用分散式鎖。
我們使用快取(Tair或者Redis)實現分散式鎖,具體程式碼如下:
@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,並且在後續的呼叫中不再改變。
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變數,則該變數的副本會提交給垃圾回收器。