在我的工作中,我偶然发现了这样的设计问题:
Manager
类的实例如果存在这样的事情,前两点会使它成为“每线程单身”。
这是我提出的(代码简化,我省略了安全检查等):
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函数。 另请注意,通过使用静态函数,实例的引用永远不会泄露,因此分配给不同线程的实例不会混淆。
这很方便,但后来又有了另一项要求:
所以我定义了一个界面:
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类在这里作为外观工作,将所有调用转发到适当的处理程序。这种方法存在一个大问题:我需要在Manager
类和ManagerHandler
接口中定义所有函数。不幸的是,Manager
类无法实现ManagerHandler
接口,因为它具有静态函数而不是方法。
问题是:你能否想到一个更好/更简单的方法来实现我上面列出的所有目标,而这个目标可以解决这个问题?
答案 0 :(得分:1)
您无能为力,因为您基本上需要通过静态方法代理接口方法。我只能想到两种不同方式实现相同功能的方法:
Manager
并使用ManagerHandler
的注入实现,其中包含ThreadLocal
。ManagerAccess
界面中的方法生成(如'字节码生成')静态ManagerHandler
类。就个人而言,我不会考虑将静态ManagerAccess
类(包含ThreadLocal
)作为严重的设计问题。至少只要它保持自己的职责集(访问线程范围的实例和代理调用),并且不冒险在其他任何地方。
答案 1 :(得分:1)
如果您正在使用此设计,Manager
是否有必要完全隐藏ManagerHandler
界面,或者您是否可以公开它以便您不必委托每个方法?
class Manager {
public static ManagerHandler getHandler() { return local.get(); }
}
答案 2 :(得分:-1)
为每个线程类创建单例的技巧是在私有静态_current字段上使用ThreadStatic属性,这使得它由线程限定。这样,_current字段将存储在线程内存中,而其他线程无法访问,而AppDomain则不是共享内存。因此,它仅在线程范围内可用。另一方面,Current属性可以在该AppDomain中的所有线程上访问,但是当它被调用时,它将返回该线程的正确实例。以下是您需要的代码:
public sealed class Manager
{
// As you are using the ThreadStatic here you cannot
// call the static constructor or use the Lazy implimentation for
// thread-safty and you have to use the old fashin Lock and anti-pattern.
private static readonly object _criticalArea = new object();
[ThreadStatic]
private static Manager _current;
public static Manager Current
{
get
{
if (_current == null)
{
lock (_criticalArea)
{
if (_current == null)
{
_current = new Manager();
}
}
}
return _current;
}
}
private Manager()
{
}
public string WhatThreadIsThis { get; set; }
}
[TestClass]
public class SingeltonPerThreadTest
{
private readonly EventWaitHandle _threadHandler = new EventWaitHandle(false, EventResetMode.AutoReset);
private string _sharedMemory = "I am the shared memory and yet in main thread :(";
[TestMethod]
public void TestSingeltonPerThread()
{
// Creates a _current for main thread.
Manager.Current.WhatThreadIsThis = "I am the main thread :)";
// Start another thread.
(new Thread(CallTheThreadBaseSingelton)).Start();
// Wait for it to be finished.
_threadHandler.WaitOne();
Assert.AreEqual("I am the main thread :)", Manager.Current.WhatThreadIsThis, "I am not the main thread :( ");
Assert.AreEqual("I am the other thread ;)", _sharedMemory, _sharedMemory);
}
private void CallTheThreadBaseSingelton()
{
// Creates a _current for this thread (this thread is the other one :)) ).
Manager.Current.WhatThreadIsThis = "I am the other thread ;)";
_sharedMemory = Manager.Current.WhatThreadIsThis;
_threadHandler.Set();
}
}
干杯。