I am looking for a good way to write an instance factory that creates exactly one instance of a class for each thread (in other words: a thread-specific singleton).
Let me add some example code for clarity:
First, an interface that defines a factory in general:
public interface Factory<T> {
public T create();
}
For normal singletons, I can then create an implementation of this interface that wraps another factory:
public class SingletonFactory<T> implements Factory<T> {
private final Factory<T> factory;
private T instance = null;
public SingletonFactory(Factory<T> factory) {
this.factory = factory;
}
@Override
public T create() {
if (instance==null) instance = factory.create();
return instance;
}
}
Basically, the first time that create()
is called, this call is forwarded to the provided factory to create one instance of the object. This instance is cached and all future calls to create()
will return that same instance. All I need to do is to make sure that there is only one instance of SingletonFactory
around per object-type T
.
Let's say I want to provide exactly one instance of the object : I could do something like this: 提供对象的一个实例:我可以做这样的事情:
public class ThreadSingletonFactory<T> implements Factory<T> {
private final Factory<T> factory;
private final Map<Thread, T> instance;
public ThreadSingletonFactory(Factory<T> factory) {
this.factory = factory;
this.instance = new HashMap<Thread, T>();
}
@Override
public T create() {
Thread thread = Thread.currentThread();
T result = instance.get(thread);
if (result==null) {
result = factory.create();
instance.put(thread, result);
}
return result;
}
}
Now, every time create()
is called, the class looks up the current thread in its instance map to see if this thread already has an instance created. If not, it creates a fresh one and remembers it.
I see a couple of issues with this naive approach:
Collections.synchronizedMap
to make it thread safe, but I would like to avoid that since it implies tons of synchronizations everywhere which might have a significant performance impact. I was looking into using a WeakHashMap
instead to address the second issue, since it allows its entries to be garbage collected once the keys are no longer in use, but I came across two potential issues with that as well:
OpenJDK Source
, the freeing of unused keys via expungeStaleEntries()
is initiated every time I call get(...)
or put(...)
and involves potentially multiple synchronized operations, which, again, I would like to avoid for performance reasons. Is there a different solution that preferably does not need to use synchronization
(assuming that all implementations of Factory.create()
are thread-safe)? synchronization
(假设Factory.create()
所有实现都是线程安全的)? If it does need to manage concurrency somehow, I would prefer it to do so via the classes in java.util.concurrent.atomic
.
As suggested by @JBNizet and @SotiriosDelimanolis, ThreadLocal might do the trick.
I haven't tested it yet, but this might be it then?
public class ThreadSingletonFactory<T> implements Factory<T> {
private final ThreadLocal<T> instance;
public ThreadSingletonFactory(final Factory<T> factory) {
this.instance = new ThreadLocal<T>() {
@Override
protected T initialValue() {
return factory.create();
}
};
}
@Override
public T create() {
return instance.get();
}
}
I have used the implementation as explained above, I am able to achieve the single object per thread.
Below is my complete implementation
1. Created one singleton class MySingleTon with threadlocal object
2. Created 2 thread class to use the MySingleTon object in the run method
3. Created one class to create the thread object and also the MySingleTon object
4. From the sysout statement, all MySingleTon reference are pointing to the same object
package example.test;
public class ThreadLevelSingleton
{
public static void main(String[] args)
{
Thread33 t3 = new Thread33();
t3.start();
Thread44 t4 = new Thread44();
t4.start();
MySingleTon m1 = MySingleTon.getInstance();
MySingleTon m2 = MySingleTon.getInstance();
System.out.println(Thread.currentThread().getName() + " : " + (m1 == m2));
MySingleTon tm1 = t3.getMySingleTon();
MySingleTon tm2 = t4.getMySingleTon();
System.out.println(Thread.currentThread().getName() + " : " + (tm1.equals(tm2)));
}
}
class MySingleTon
{
private MySingleTon()
{
}
private static final ThreadLocal<MySingleTon> t = new ThreadLocal<MySingleTon>()
{
@Override
protected MySingleTon initialValue()
{
return new MySingleTon();
}
};
public static MySingleTon getInstance()
{
return t.get();
}
}
class Thread33 extends Thread
{
MySingleTon m1;
@Override
public void run()
{
MySingleTon t = MySingleTon.getInstance();
MySingleTon t1 = MySingleTon.getInstance();
m1 = MySingleTon.getInstance();
System.out.println(getName() + " : " + (t == t1));
System.out.println(t);
System.out.println(t1);
}
MySingleTon getMySingleTon()
{
return m1;
}
}
class Thread44 extends Thread
{
MySingleTon m1;
@Override
public void run()
{
MySingleTon t = MySingleTon.getInstance();
MySingleTon t1 = MySingleTon.getInstance();
m1 = MySingleTon.getInstance();
System.out.println(getName() + " : " + (t == t1));
System.out.println(t);
System.out.println(t1);
}
MySingleTon getMySingleTon()
{
return m1;
}
}
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.