2012-02-17 75 views
1

我正在处理下面的一段代码。两个线程需要他们自己的单例实例。线程本地是一个明显的解决方案。不过,我仍然面临使用自己的本地副本运行线程的问题。我在几个java类中有一个例子。ThreadLocal与单身人士

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的包装调用的getInstance在辛格尔顿类我不每次都得到一个新的实例?我如何为我的目的做这项工作?

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

+3

'两个线程需要他们自己的单例实例'根本没有任何意义... – home 2012-02-17 11:26:26

+1

@ user2864740 - 为什么不呢?因为单例的定义是在一个系统中只有一个实例*。系统!=线程。如果你想让人们理解你(和OP)想说的话,你需要常规地使用术语。例如参考维基百科。 – 2015-02-11 22:29:31

+0

如果你知道你只需要对象的两个实例,那么使用[doubleton pattern](https://coderanch.com/t/509003/java/java-doubleton-pattern) – Laxman 2016-12-19 09:29:45

回答

-1

使用同步进行多线程。

public static synchronized final Singleton getInstance() { 

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

另外,你不需要2个单身(其中恕我直言,实际上没有任何意义,并打败了单身人士自己的目的...)。

+0

由于我正在使用的代码别无选择,只能不幸地使用单身。我使用ThreadLocal使我能够创建每个线程单身人士,因为我相信他们是为了这个目的而创建的。 – TFlanagan 2012-02-17 12:09:37

+0

你为什么相信你必须使用单身?你能否详细解释一下问题本身? – 2012-02-17 12:18:45

+0

我没有说你不必使用singeltons ......如果你使用ThreadLocal,那么问题是什么?如果问题是如果你没有得到一个新的实例,那么答案是否定的,因为如果它已经实例化,你返回实例,如果没有,你实例化,然后返回实例。 – m0skit0 2012-02-17 12:27:45

1

你的目标类不应该是singleton,而是你必须使用ThreadLocal来访问它,并且如果ThreadLocal实例是空的(不包含对你的目标对象的实例的引用)则创建一个新的实例。

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

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(); 
} 
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; 
    } 
} 
1

你似乎被画成一个角落。

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

另一方面,您的测试需要您编写一个包含许多这些Singleton1实例的客户端。

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

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

(例如,在实施ThreadLocalSingleton1您当前的尝试,因为Singleton1.getInstance()返回Singleton1全局实例失败。不管你做什么,有没有办法创建Singleton1类的任何其他实例。)

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

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

这里有解决方法:

  1. 运行一个JVM与测试线的N个实例相反的,运行N各自不同JVM有一个单独的测试线。每个JVM /测试线程可以有自己的实例Singleton

  2. 让每个测试的线程创建一个新的类加载器,并用它的ClassLoader动态负载Singleton1类的一切与在Singleton1型有直接或间接的静态依赖。这个想法是为每个类加载器加载它自己的Singleton1类的副本。由于每个副本将是不同类型的,因此它将拥有自己的private static Singleton1 instance变量。

请注意,这些变通办法不提供Singleton1类的“线程本地”实例。这在技术上是不可能的......而且与单身人士的定义相矛盾。

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


1 - 在运行时,一个类的实例的类型在概念上是对由所述类的完全合格的名称和加载的类类加载器的身份。如果相同的字节码文件是由不同的类加载器加载的,则会得到不同的运行时类型。