每线程单例模式
在我的工作中,我偶然发现了这样一个设计问题:
- 每个线程我需要一个 Manager 类的实例
- 这些实例应该是全局可访问的,就像通过静态函数在单例模式中一样
- 每个线程可能需要初始化具有不同参数的实例
- 这些实例的生命周期应该是可控的,有时删除实例并允许 GC 收集它会是有益的。
如果存在这种情况,前两点将使其成为“每线程单例”。
这就是我的想法(代码被简化,我省略了安全检查等):
public class Manager {
private final static ThreadLocal<Manager> local = new ThreadLocal<Manager>();
private int x;
Manager(int argument) { x = argument; }
public static void start(int argument) { local.set(new Manager(argument); }
public static void clean() { local.remove(); }
private void doSomething1() { x++; .... }
private int doSomething2() { if (--x == 0) clean(); ... }
public static void function1() { local.get().doSomething1(); }
public static int function2() { return local.get().doSomething2(); }
}
正如您所看到的,也可以从私有方法中调用 clean 函数。 另请注意,通过使用静态函数,对实例的引用永远不会泄漏,因此分配给不同线程的实例不会混合。
这工作得很好,但后来我得到了另一个要求:
- 不同的线程可能需要利用 Manager 类的不同实现
所以我定义了一个接口:
public interface ManagerHandler {
void method1();
int method2();
}
并修改了 Manager 类:
public class Manager {
private final static ThreadLocal<ManagerHandler> local = new ThreadLocal<ManagerHandler>();
public static void start(int argument) {
ManagerHandler handler;
// depending on the context initialize handler to whatever class it is necessary
local.set(handler);
}
public static void clean() { local.remove(); }
public static void function1() { local.get().method1(); }
public static int function2() { return local.get().method2(); }
}
示例实现如下所示:
public class ExampleManagerImplementation implements ManagerHandler {
private int x;
public ExampleManagerImplementation(int argument) { x = argument; }
public void method1() { x++; .... }
public int method2() { if (--x == 0) Manager.clean(); ... }
}
管理器类在这里作为外观,将所有调用转发到适当的处理程序。这种方法有一个大问题:我需要在 Manager
类和 ManagerHandler
接口中定义所有函数。不幸的是,Manager
类无法实现 ManagerHandler
接口,因为它具有静态函数而不是方法。
问题是:您能想出一种更好/更简单的方法来实现我上面列出的所有目标,并且不会出现此问题吗?
In my work I stumbled upon such a design issue:
- I need one instance of a
Manager
class per thread - These instances should be globally accessible, like in the singleton pattern via a static function
- Each thread might need to initialize its instance with different arguments
- The lifetime of these instances should be controllable, sometimes it would be beneficiary to remove an instance and allow GC to collect it
The first two points would make it a 'per thread singleton' if such a thing exists.
This is what I came up with (the code is simplified, I've omitted safety checks and so on):
public class Manager {
private final static ThreadLocal<Manager> local = new ThreadLocal<Manager>();
private int x;
Manager(int argument) { x = argument; }
public static void start(int argument) { local.set(new Manager(argument); }
public static void clean() { local.remove(); }
private void doSomething1() { x++; .... }
private int doSomething2() { if (--x == 0) clean(); ... }
public static void function1() { local.get().doSomething1(); }
public static int function2() { return local.get().doSomething2(); }
}
As you can see the clean function can be also called from within the private methods.
Also notice that through the use of static functions the reference to the instance is never leaked, so instances assigned to different threads won't get mixed.
This works quite ok, but then I got another requirement:
- Different threads may need to utilize different implementations of Manager class
So I defined an interface:
public interface ManagerHandler {
void method1();
int method2();
}
And modified the Manager
class:
public class Manager {
private final static ThreadLocal<ManagerHandler> local = new ThreadLocal<ManagerHandler>();
public static void start(int argument) {
ManagerHandler handler;
// depending on the context initialize handler to whatever class it is necessary
local.set(handler);
}
public static void clean() { local.remove(); }
public static void function1() { local.get().method1(); }
public static int function2() { return local.get().method2(); }
}
An example implementation would look like this:
public class ExampleManagerImplementation implements ManagerHandler {
private int x;
public ExampleManagerImplementation(int argument) { x = argument; }
public void method1() { x++; .... }
public int method2() { if (--x == 0) Manager.clean(); ... }
}
Manager class works here as a facade, forwarding all the calls to the appropriate handler. There is one big issue with this approach: I need to define all the functions both in the Manager
class and in the ManagerHandler
interface. Unfurtunately Manager
class can't implement ManagerHandler
interface, because it has static functions rather than methods.
The question is: can you think of a better/easier way to accomplish all the goals I've listed above that would be free of this issue?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
您无能为力,因为您基本上需要通过静态方法代理接口方法。我只能想到两种方法来以不同的方式实现相同的功能:
Manager
并使用ManagerHandler
的注入实现code> 将包含 ThreadLocal。ManagerHandler
接口中的方法生成(如“字节码生成”)静态ManagerAccess
类。就我个人而言,我不认为静态
ManagerAccess
类(其中包含ThreadLocal
)是一个严重的设计问题。至少只要它遵守自己的职责(访问线程范围的实例和代理调用)并且不冒险到其他地方。There is not much you can do, as you basically need to proxy interface methods through static methods. I could only think of two ways to achieve the same functionality differently:
Manager
and use an injected implementation ofManagerHandler
which will contain theThreadLocal
.ManagerAccess
class using the methods found in theManagerHandler
interface.Personally, I wouldn't think of having the static
ManagerAccess
class (which contains theThreadLocal
) around as a serious design issue. At least as long as it keeps to its own set of responsibilities (accessing thread-scoped instances and proxying calls) and doesn't venture anywhere else.如果您要采用这种设计,
Manager
是否有必要完全隐藏ManagerHandler
接口,或者您可以公开它以便不必委托每个方法吗?If you're going with this design, is it necessary for
Manager
to totally hideManagerHandler
interface, or could you expose it so you don't have to delegate every method?每个线程类创建单例的技巧是使用 ThreadStatic 私有 static _current 字段上的属性,使其范围为 线。这样,_current 字段将存储在线程内存中,其他线程无法访问该内存,也不是 AppDomain 的共享内存。因此,它仅在线程范围内可用。另一方面,Current 属性可以在该 AppDomain 中的所有线程中访问,但是当调用它时,它将返回该线程的正确实例。这是您需要的代码:
干杯。
The trick for creating a singleton per thread class is to use ThreadStatic attribute on your private static _current field which makes it scoped by thread. In this way, the _current field will be stored inside thread memory which is not accessible for the other threads and not shared memory of AppDomain. So, it will be available only in the scope of the thread. On the other hand, the Current property is accessible across all threads in that AppDomain but when it is called it will return the correct instance for that thread. Here is the code that you need:
Cheers.