threadlocal原理及常用应用场景2

来源:互联网 发布:化工公司工艺软件 编辑:程序博客网 时间:2024/06/06 03:37
最近项目中遇到如下的场景:在执行数据迁移时,需要按照用户粒度加锁,因此考虑使用排他锁,迁移工具和业务服务属于两个服务,因此需要使用分布式锁。

我们使用缓存(Tair或者Redis)实现分布式锁,具体代码如下:

  1. @Service  
  2. public class Locker {  
  3.     @Resource(name = "tairClientUtil")  
  4.     private TairClientUtil tairClientUtil;  
  5.   
  6.     private ThreadLocal<Long> lockerBeanThreadLocal = new ThreadLocal<>();  
  7.   
  8.     public void init(long userid) {  
  9.         lockerBeanThreadLocal.remove();  
  10.         lockerBeanThreadLocal.set(userid);  
  11.     }  
  12.   
  13.     public void updateLock() {  
  14.         String lockKey = Constants.MIGRATION_PREFIX + lockerBeanThreadLocal.get();  
  15.         tairClientUtil.incr(lockKey, Constants.COUNT_EXPIRE);  
  16.     }  
  17.   
  18.     public void invalidLock() {  
  19.         String lockKey = Constants.MIGRATION_PREFIX + lockerBeanThreadLocal.get();  
  20.         tairClientUtil.invalid(lockKey);  
  21.     }  
  22. }  
  23.   
  24. 作者:杜琪  
  25. 链接:http://www.jianshu.com/p/cadd53f063b9  


因为每个线程可能携带不同的userid发起请求,因此在这里使用ThreadLocal变量存放userid,使得每个线程都有一份自己的副本。

参考官方文档:ThreadLocal的用法,这个类提供“thread-local”变量,这些变量与线程的局部变量不同,每个线程都保存一份改变量的副本,可以通过get或者set方法访问。如果开发者希望将类的某个静态变量(user ID或者transaction ID)与线程状态关联,则可以考虑使用ThreadLocal。

举个例子,下面的类为每个线程生成不同的ID,当某个线程第一次调用Thread.get()时,会为该线程赋予一个ID,并且在后续的调用中不再改变。

 

  1. import java.util.concurrent.atomic.AtomicInteger;  
  2.   
  3.  public class ThreadId {  
  4.      // Atomic integer containing the next thread ID to be assigned  
  5.      private static final AtomicInteger nextId = new AtomicInteger(0);  
  6.   
  7.      // Thread local variable containing each thread's ID  
  8.      private static final ThreadLocal<Integer> threadId =  
  9.          new ThreadLocal<Integer>() {  
  10.              @Override protected Integer initialValue() {  
  11.                  return nextId.getAndIncrement();  
  12.          }  
  13.      };  
  14.   
  15.      // Returns the current thread's unique ID, assigning it if necessary  
  16.      public static int get() {  
  17.          return threadId.get();  
  18.      }  
  19.  }  
  20.   
  21.  
  22.   每个线程会“隐式”包含一份thread-local变量的副本,只要线程还处于活跃状态,就可以访问该变量;当线程停止后,如果没有其他线程持有该thread-local变量,则该变量的副本会提交给垃圾回收器。 
  23.  
  24. /** 
  25.  * {@link ThreadLocal} subclass that exposes a specified name 
  26.  * as {@link #toString()} result (allowing for introspection). 
  27.  * 
  28.  * @author Juergen Hoeller 
  29.  * @since 2.5.2 
  30.  * @see NamedInheritableThreadLocal 
  31.  */  
  32. public class NamedThreadLocal<T> extends ThreadLocal<T> {  
  33.   
  34.     private final String name;  
  35.   
  36.   
  37.     /** 
  38.      * Create a new NamedThreadLocal with the given name. 
  39.      * @param name a descriptive name for this ThreadLocal 
  40.      */  
  41.     public NamedThreadLocal(String name) {  
  42.         Assert.hasText(name, "Name must not be empty");  
  43.         this.name = name;  
  44.     }  
  45.   
  46.     @Override  
  47.     public String toString() {  
  48.         return this.name;  
  49.     }  
  50.   
  51. }  
  52.  
  53. XmlBeanDefinitionReader.java  
  54.   
  55. private final ThreadLocal<Set<EncodedResource>> resourcesCurrentlyBeingLoaded =  
  56.             new NamedThreadLocal<Set<EncodedResource>>("XML bean definition resources currently being loaded");  
  57.   
  58.     /** 
  59.      * Load bean definitions from the specified XML file. 
  60.      * @param encodedResource the resource descriptor for the XML file, 
  61.      * allowing to specify an encoding to use for parsing the file 
  62.      * @return the number of bean definitions found 
  63.      * @throws BeanDefinitionStoreException in case of loading or parsing errors 
  64.      */  
  65.     public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {  
  66.         Assert.notNull(encodedResource, "EncodedResource must not be null");  
  67.         if (logger.isInfoEnabled()) {  
  68.             logger.info("Loading XML bean definitions from " + encodedResource.getResource());  
  69.         }  
  70.   
  71.         Set<EncodedResource> currentResources = this.resourcesCurrentlyBeingLoaded.get();  
  72.         if (currentResources == null) {  
  73.             currentResources = new HashSet<EncodedResource>(4);  
  74.             this.resourcesCurrentlyBeingLoaded.set(currentResources);  
  75.         }  
  76.         if (!currentResources.add(encodedResource)) {  
  77.             throw new BeanDefinitionStoreException(  
  78.                     "Detected cyclic loading of " + encodedResource + " - check your import definitions!");  
  79.         }  
  80.         try {  
  81.             InputStream inputStream = encodedResource.getResource().getInputStream();  
  82.             try {  
  83.                 InputSource inputSource = new InputSource(inputStream);  
  84.                 if (encodedResource.getEncoding() != null) {  
  85.                     inputSource.setEncoding(encodedResource.getEncoding());  
  86.                 }  
  87.                 return doLoadBeanDefinitions(inputSource, encodedResource.getResource());  
  88.             }  
  89.             finally {  
  90.                 inputStream.close();  
  91.             }  
  92.         }  
  93.         catch (IOException ex) {  
  94.             throw new BeanDefinitionStoreException(  
  95.                     "IOException parsing XML document from " + encodedResource.getResource(), ex);  
  96.         }  
  97.         finally {  
  98.             currentResources.remove(encodedResource);  
  99.             if (currentResources.isEmpty()) {  
  100.                 this.resourcesCurrentlyBeingLoaded.remove();  
  101.             }  
  102.         }  
  103.     } 

原创粉丝点击