我正在寻找一种编写实例工厂的好方法,该工厂为每个线程创建一个类的实例(换句话说:特定于线程的单例)。
为了清楚起见,让我添加一些示例代码:
首先,一个一般定义工厂的接口(interface):
public interface Factory<T> {
public T create();
}
对于普通的单例,我可以创建一个包装另一个工厂的接口(interface)的实现:
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;
}
}
基本上,第一次调用 create()
时,此调用将转发到提供的工厂以创建对象的一个实例。该实例已被缓存,以后对 create() 的所有调用都将返回同一实例。我需要做的就是确保每个对象类型 T
周围只有一个 SingletonFactory
实例。
假设我想每个线程提供一个对象实例:我可以这样做:
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;
}
}
现在,每次调用 create()
时,该类都会在其实例映射中查找当前线程,以查看该线程是否已创建实例。如果没有,它会创建一个新的并记住它。
我发现这种幼稚的方法存在一些问题:
- HashMap 不是线程安全的,这可能不是问题,因为没有两个线程会操作同一个键,但我不确定。我可以使用
Collections.synchronizedMap
使其线程安全,但我想避免这种情况,因为它意味着到处都有大量同步,这可能会对性能产生重大影响。 - 如果应用程序不使用线程池,但不断生成大量短期的新线程,则映射将增长到潜在的巨大尺寸,并用不再需要的实例阻塞大量内存。
我正在考虑使用 WeakHashMap
相反,它是为了解决第二个问题,因为一旦 key 不再使用,它允许其条目被垃圾收集,但我也遇到了两个潜在的问题:
- 查看
OpenJDK Source
,通过expungeStaleEntries()
释放未使用的 key 每次我调用get(...)
或put(...)
时都会启动并涉及潜在的多个同步操作,我再次希望避免这种情况性能原因。 - 我仍然不确定是否还需要将映射包装到同步映射中以确保不会遇到并发问题。
是否有一种不同的解决方案,最好不需要使用任何 同步
(假设Factory.create()
的所有实现都是线程安全)?如果它确实需要以某种方式管理并发,我更希望它通过 java.util.concurrent.atomic
中的类来这样做。 .
最佳答案
根据@JBNizet 和@SotiriosDelimanolis 的建议,ThreadLocal可能会成功。
我还没有测试过,但可能就是这样了?
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();
}
}
关于java - 线程特定的单例实例,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/24599136/