ThreadLocal与单身人士

时间:2012-02-17 11:24:27

标签: java multithreading

我正在处理以下代码。两个线程需要他们自己的单例实例。 Thread Local是一个明显的解决方案。但是,我仍然面临使用自己的本地副本运行线程的问题。我在几个java类中有一个场景示例。

public class Singleton1 {

private int i = 0;

private static Singleton1 instance;

private Singleton1() {
}

public static final Singleton1 getInstance() {
    if (instance == null) {
        instance = new Singleton1();
    }
    return instance;
}

public int increment() {
    return i++;
}

}

public class Holder1 {

private final Singleton1 instance;

public Holder1() {
    ThreadLocalSingleton1 singleton1 = new ThreadLocalSingleton1();
    instance = singleton1.get();
}

public int increment() {
    return instance.increment();
}

private class ThreadLocalSingleton1 extends ThreadLocal<Singleton1> {

    @Override
    protected Singleton1 initialValue() {
        return Singleton1.getInstance();
    }

}

}

public class HolderTest {

/**
 * @param args
 */
public static void main(String[] args) {
    HolderTest test = new HolderTest();
    HolderThread thread1 = test.getHolderThread("thread1");
    HolderThread thread2 = test.getHolderThread("thread2");
    thread1.run();
    thread2.run();

}

public HolderThread getHolderThread(String name) {
    return new HolderThread(name);
}

private class HolderThread implements Runnable {
    String name;

    Holder1 holder1 = new Holder1();

    public HolderThread(String name) {
        this.name = name;
    }

    @Override
    public void run() {
        while (true) {
            System.out.println(name + " " + holder1.increment());
        }
    }
}

当ThreadLocal包装器在Singleton类上调用getInstance时,我每次都没有获得新实例?我如何为我的目的做这项工作?

上面的代码是我正在使用的实际代码的简单版本。我有单身人士课程,我不能从单身人士改变。我正在创建一个测试客户端,它需要作为单个进程运行,但需要多个线程。这些线程中的每一个都需要拥有自己的这些单例实例。

5 个答案:

答案 0 :(得分:1)

您的目标类不应该是单例,但您必须使用ThreadLocal访问它,并在ThreadLocal实例为空时创建新实例(不保存对目标对象实例的引用)。

另一个解决方案是让你的Target类单例,并在ThreadLocal变量中保持其状态。

答案 1 :(得分:1)

你似乎被画成了一个角落。

一方面,您有一个需要测试的现有代码库,并且该代码使用(真正的,正确实现的)单例对象。特别是,在您的示例类Singleton1()中将private构造函数声明为Singleton1使得无法声明子类。

另一方面,您的测试要求您编写一个包含大量Singleton1个实例的客户端。

从表面上看,这是不可能的。无法在JVM中创建Singleton1类的两个实例,并且无法声明Singleton1的(可编译/可加载)子类。

这是每个设计;即它是Singleton1类的设计者所期望的。 (如果没有,那么答案就是更改Singleton1以便更容易测试。例如,通过使Singleton1构造函数不是private,以便可以创建多个实例进行测试目的。)

(例如,您当前尝试实施ThreadLocalSingleton1失败,因为Singleton1.getInstance()返回Singleton1的全局实例。无论您做什么,都无法创建 Singleton1类的任何其他实例。)

但是,我可以为您的特定用例考虑两种解决方法。

  

我正在编写一个需要像单个java进程一样运行的测试客户端。测试客户端用于负载测试将有X个线程使用核心项目(我不能改变太多)访问服务器,这个项目有很多单例。单身人员持有每个线程所需的状态。

以下是解决方法:

  1. 不是运行一个带有N个测试线程实例的JVM,而是运行N个单独的JVM,每个JVM都有一个测试线程。每个JVM /测试线程都可以拥有自己的Singleton

  2. 实例
  3. 让每个测试线程创建一个新的类加载器,并使用该类加载器动态加载Singleton1类以及对Singleton1类型具有直接或间接静态依赖性的所有内容。这个想法是每个类加载器加载自己的Singleton1类副本。由于每个副本都是一个不同的类型 1 ,因此它将拥有自己的private static Singleton1 instance变量。

  4. 请注意,这些变通办法确实没有提供&#34;线程本地&#34; Singleton1班级的实例。这在技术上是不可能的......而且与单身人士的定义相矛盾。

    在这两种情况下,您都拥有真正的单例实例,但它们是不同 Singleton1类型的实例......出于不同的原因。

    1 - 在运行时,类实例的类型在概念上是一对由类的完全限定名称和加载类的类加载器的标识组成。如果不同的类加载器加载了相同的字节码文件,那么您将获得不同的运行时类型。

答案 2 :(得分:0)

你的意思是这样吗?

private static final ThreadLocal<AtomicInteger> COUNTER = new ThreadLocal<AtomicInteger>() {
    @Override
    protected AtomicInteger initialValue() {
        return new AtomicInteger();
    }
};

public static int incrementAndGet() {
    return COUNTER.get().incrementAndGet();
}

答案 3 :(得分:0)

请查看下面的ThreadLocal工作示例:

public class YourDataHolder {
    private static ThreadLocal dataVariable = new ThreadLocal();
    private static YourDataHolder dataHolderVar;
    private YourDataHolder() { }
    public void storeDataToThreadLocal (String userName) {
        dataVariable.set(userName);
    }
    public String readDataFromThreadLocal ()  {
        if (dataVariable.get() != null) {
            return (String) dataVariable.get();
        }
    }
    public static ServiceVersionHolder getInstance () {
        if (dataHolderVar == null) {
         dataHolderVar = new YourDataHolder();
        }
        return dataHolderVar;
    }
}

答案 4 :(得分:-1)

使用 synchronized 进行多线程处理。

public static synchronized final Singleton getInstance() {

这样线程将“锁定”该方法:一次只允许一个线程进入该方法,其他线程将阻塞,直到该方法被解锁(执行它的线程离开)。你不会遇到那些并发问题。

此外,你不需要2个单身人士(恕我直言,实际上没有任何意义,并且违背了单身人士自己的目的......)。