在Java中使用ThreadLocal的良好实践

时间:2014-12-17 22:41:14

标签: java concurrency parallel-processing thread-local

我对如何使用ThreadLocal提出了疑问。

背景和情境

有几个单例对象使用ThreadLocal为每个线程创建一个副本。该单例对象具有函数foo()

public class SingletonA {
    protected static ThreadLocal<SingletonA> singleton = new ThreadLocal<SingletonA>() {
        @Override
        protected SingletonA initialValue() {
            return new SingletonA();
        }
    };

    private SingletonA() { ... }
    public static SingletonA getInstance() { return singleton.get(); }
    public static void remove() { singleton.remove(); }
    public static void foo() { ... }
}

......有SingletonB,SingletonC等等。

有一个单例存储库可以缓存上面的ThreadLocal单例。这个类也是ThreadLocal单例 -

public class SingletonRepo {
        protected static ThreadLocal<SingletonRepo> singleton = new ThreadLocal<SingletonRepo>() {
        @Override
        protected SingletonRepo initialValue() {
            return new SingletonRepo();
        }
    };

    private SingletonRepo() { ... }
    public static SingletonRepo getInstance() { return singleton.get(); }
    public static void remove() { singleton.remove(); }

    public SingletonA singletonA;
    // same thing for singletonB, singletonC, ...

   public static init() {
        // Caching the ThreadLocal singleton
        singletonA = SingletonA.getInstance();
        // Same thing for singletonB, singletonC ...
   }
}

这是我目前通过ThreadLocal

访问SingletonRepo单身人士的方式
public class App {
    public SingletonRepo singletonRepo;
    public static void main(String [] args) {
        singletonRepo = SingletonRepo.getInstance();
        singletonRepo.init();

        singletonRepo.singletonA.helperFunction();
    }
}

问题

正如您在上面的上下文中看到的,为了访问ThreadLocal单身人士,我首先将其缓存在SingletonRepo中。当我需要使用ThreadLocal单例时,我从缓存引用中获取它。我有以下问题 -

  1. 通过缓存副本访问ThreadLocal单例是不好的做法吗?
  2. 通过ThreadLocal(为Singleton对象调用SingletonA.getInstance())始终访问get()单例是否为更好的做法?

2 个答案:

答案 0 :(得分:1)

线程本地缓存副本很好,因为它更简单,更高效。一个缓存副本,你不确定它是本地线程可能是一个问题。

单身定义意味着只能有一个。我不会把你作为一个单身人所拥有的,每个线程都有一个。

我会在它的构造函数中初始化()你的线程局部对象。

BTW你的ThreadLocal应该是private static final我怀疑。

答案 1 :(得分:0)

目标还不是很清楚。 ThreadLocal查找的性能方面可以忽略不计,因此“缓存”一个值没有多大意义,尤其是当您仍然需要查找缓存实例时。

如果SingletonASingletonBSingletonC等类型没有ThreadLocal,那么SingletonRepo是唯一的,那么它会更有用实例通过ThreadLocal维护,而其他实例由SingletonRepo维护,因此隐式线程本地。但是,为了以这种方式实现,SingletonASingletonBSingletonC等的构造函数必须由SingletonRepo访问。

鉴于您目前实现它的方式,让我印象深刻的是单例类和repo类上存在remove方法,因此允许不一致。例如,我可以调用SingletonRepo.getInstance().init(),然后调用SingletonA.remove()和etvoilà您已创建当前线程的情况,其中SingletonRepo.getInstance().singletonA包含对实例的引用,SingletonA.singleton不保持一个值,并在下一次调用getInstance()时创建一个新实例,以便同一个线程同时存在两个不同的实例。