Java 如何使应用程序构建块线程安全?
我有很多遗留代码,这些代码在很大程度上由具有以下结构的类组成:Java 如何使应用程序构建块线程安全?,java,multithreading,thread-safety,Java,Multithreading,Thread Safety,我有很多遗留代码,这些代码在很大程度上由具有以下结构的类组成: public interface MyFunctionalBlock { // Setters for the inputs void setInput1(final int aInput1); void setInput2(final Object aInput2); // Inside the method run inputs are converted into results vo
public interface MyFunctionalBlock
{
// Setters for the inputs
void setInput1(final int aInput1);
void setInput2(final Object aInput2);
// Inside the method run inputs are converted into results
void run();
// If this building block needs functionality from some other building blocks,
// it gets a reference to them from the Google Guice injector.
void setInjector(final Injector aInjector);
// Getters for the results
long getResult1();
Object getResult2();
Map<String,String> getResult3();
}
public class MyFunctionalBlockFactory implements Factory<MyFunctionalBlock>
{
public MyFunctionalBlock create()
{
return new DefaultMyFunctionalBlock();
}
}
class DefaultMyFunctionalBlock implements MyFunctionalBlock
{
private int input1;
private Object input2;
private long result1;
private long result2;
private Map<String,String> result3;
private Injector injector;
@Override
public void run()
{
// Here the calculations are performed.
// If this functional block needs another one, it gets a reference to it using the injector.
// AnotherFunctionalBlock is the public interface. Implementations of the interface are
// intentionally hidden using injector and package-private declaration.
final AnotherFunctionalBlock fb = injector.getInstance(AnotherFunctionalBlock.class);
// First, we set the inputs
fb.setInput1(...);
fb.setInput2(...);
[...]
fb.setInputN(...);
// Now we run the calculation
fb.run();
// Now we can use the results
fb.getResult1();
fb.getResult2();
[...]
fb.getResultN();
}
// Implementation of getters and setters omitted
}
更新1(09.06.2013 21:57 MSK):一个潜在的重要注意事项-并发性源于这样一个事实:有N个web服务接收请求,然后使用旧代码根据该请求进行计算,并将结果返回给web服务客户端
一个潜在的解决方案是在web服务和旧代码之间添加某种队列
更新2:
我考虑了如何以尽可能少的努力使代码线程安全,并找到了以下解决方案(目前,我不关心性能)
有几个web服务类,它们都有一个后端属性并同时访问它
public class WebService1
{
private Backend backend;
public Response processRequest(SomeRequest1 request)
{
return wrapResultIntoResponse(backend.doSomeThreadUnsafeStuff1(request.getParameter1(), request.getParameter2()));
}
}
public class WebService2
{
private Backend backend;
public Response processRequest(SomeRequest2 request)
{
return wrapResultIntoResponse(backend.doSomeThreadUnsafeStuff2(request.getParameter1(), request.getParameter2(), request.getParameter3()));
}
}
所有对非线程安全代码的调用都通过后端
类进行(所有web服务都引用一个相同的后端
实例)
如果我确保后端处理一个接一个的请求(并且从不同时处理两个请求),我就可以在不重新编写整个应用程序的情况下获得所需的结果
final AnotherFunctionalBlock fb = injector.getInstance(AnotherFunctionalBlock.class);
synchronized(fb)
{
fb.setInput1(...);
fb.setInput2(...);
[...]
fb.setInputN(...);
fb.run();
fb.getResult1();
fb.getResult2();
[...]
fb.getResultN();
}
下面是我对后端类的实现:
public class Backend
{
private synchronized boolean busy = false;
public Object doSomeThreadUnsafeStuff1(Long aParameter1, String aParameter2)
{
waitUntilIdle();
synchronized (this)
{
busy=true;
// Here comes the non-thread safe stuff 1
busy=false;
notifyAll();
}
}
public Object doSomeThreadUnsafeStuff2(Long aParameter1, String aParameter2, Map<String,String> aParameter3)
{
waitUntilIdle();
synchronized (this)
{
busy=true;
// Here comes the non-thread safe stuff 2
busy=false;
notifyAll();
}
}
private void waitUntilIdle()
{
while (busy)
{
wait();
}
}
}
公共类后端
{
私有同步布尔忙=假;
公共对象doSomeThreadUnsafeStuff1(长APParameter1,字符串APParameter2)
{
waitUntilIdle();
已同步(此)
{
忙=真;
//下面是非线程安全的东西1
忙=假;
notifyAll();
}
}
公共对象doSomeThreadUnsafeStuff2(长APParameter1,字符串APParameter2,映射APParameter3)
{
waitUntilIdle();
已同步(此)
{
忙=真;
//下面是非线程安全的东西2
忙=假;
notifyAll();
}
}
私有void waitUntilIdle()
{
当(忙)
{
等待();
}
}
}
这个解决方案行得通吗?除了“使其多线程化”之外,还不清楚您想要实现什么。Java中的并发性是一个非常复杂的主题,对于如何将整个应用程序从单线程转换为多线程,您不会找到一个单一的、逐步的答案。如果你这样做,我会完全不相信这个答案。我建议你拿起,这类事情的事实参考。这就是解决这个问题所需要了解的内容。除了“使其多线程化”之外,还不清楚您想要实现什么。Java中的并发性是一个非常复杂的主题,对于如何将整个应用程序从单线程转换为多线程,您不会找到一个单一的、逐步的答案。如果你这样做,我会完全不相信这个答案。我建议你拿起,这类事情的事实参考。这就是如何了解解决此问题所需的知识。您在更新2中提出的解决方案将使整个应用程序线程安全。我将其总结为在web用户和实际业务代码之间放置一个facade/singleton层。遗留代码本身不是线程安全的,因此您必须重写它,但是由于您完全控制对它的访问并以单线程方式执行它,所以总体上是可以的。如果对底层业务代码有任何不受控制的访问,它显然会失败
你说“目前,我不在乎表现”。我希望你是对的,因为就锁争用而言,这是一个可怕的想法。但是,如果您所要做的只是将这个不安全的代码作为web服务公开,而不提供简单的访问,那么是的,将同步的单例外观放在顶部将起作用。您在更新2中提出的解决方案将使整个应用程序线程安全。我将其总结为在web用户和实际业务代码之间放置一个facade/singleton层。遗留代码本身不是线程安全的,因此您必须重写它,但是由于您完全控制对它的访问并以单线程方式执行它,所以总体上是可以的。如果对底层业务代码有任何不受控制的访问,它显然会失败 你说“目前,我不在乎表现”。我希望你是对的,因为就锁争用而言,这是一个可怕的想法。但是,如果您所要做的只是将这些不安全的代码作为web服务公开,而不提供简单的访问,那么是的,将同步的单例外观放在顶部将起作用。您在“更新2”中描述的内容与。如果您100%确信自己对性能一点也不关心——我的意思是潜在的非常糟糕的性能——并且永远不会,那么您所建议的是一个公平的解决方案,尽管您建议的实现在
忙
和等待()
的锁定(或缺少锁定)方面存在问题。看一看Actor或其他Actor框架可能会更好地为您服务
将参与者视为在单个线程中运行,并具有可以向其交付工作单元的FIFO队列。对于每一个工作单元,参与者都会以某种方式处理它,然后返回一个回复,您可以保证工作单元是串行处理的,而不是并行处理的
您称之为“后端”的是在一个或多个参与者中运行的代码,每个参与者都独立于其他参与者。这样的框架允许您采用类似于您所描述的方法,但可以扩展以提高性能,而无需花费太多精力,也无需您管理并发性。您在“更新2”中描述的内容类似于。如果你100%确信你一点也不在乎性能——我的意思是潜在的非常糟糕的性能