Java 设计模式:Factory vs Singleton?在多线程的情况下
Java design pattern: Factory vs Singleton? in case of Multithread
建议明智地创建新对象并使用相同的对象实例而不是创建新对象。我对决定在以下场景中创建对象的解决方案不是很有信心。有一个 SOAP 服务 class,它有几个方法负责多个
顾客。请看下面的模板,
Public class SOAPService {
public Object getProductList(String CustId, String endPoint){
SOAPStub stub = new SOAPStub(endPoint);
Object productList = stub.getProductList();
return productList;
}
public Object getProductInfo(String CustId, String productId, String endPoint){
SOAPStub stub = new SOAPStub(endPoint);
Object productInfo = stub.getProductList(productId);
return productInfo;
}
}
现在我介绍了一个工厂方法来为每个客户创建对象并将其放入映射中,但是当单个客户的多个线程访问该服务时我感到困惑class。对象的行为不会像单例或可能导致任何死锁问题或使线程等待吗?请赐教。
Public class SOAPService {
private Map<String, SOAPStub> map = new HashMap<String, SOAPStub>();
public SOAPStub getSOAPObject(String CustId, String endPoint){
if(map.containsKey(CustId))
return map.get(CustId);
else{
SOAPStub stub = new SOAPStub(endPoint);
map.put(custId, stub);
return stub;
}
}
public Object getProductList(String CustId, String endPoint){
SOAPStub stub = getSOAPObject(CustId, endPoint);
Object productList = stub.getProductList();
return productList;
}
public Object getProductInfo(String CustId, String productId, String endPoint){
SOAPStub stub = getSOAPObject(CustId, endPoint);
Object productInfo = stub.getProductList(productId);
return productInfo;
}
}
您的第一个代码示例看起来很安全,但是..
是的,只有在 class 服务方法在 read/write 模式下使用 class 变量时,才会为服务 class "MAY" 创建问题.
例如让我们采用以下服务方法
private int count = 0; //class variable
public Response service(Request r )
{
count = r.getSomeVariable();
...
response.setParameter(count);
}
以上方法可能不安全,因为每个请求都有自己的线程,并且还共享公共变量 "count" 当同时调用两个请求时,它们可能会覆盖彼此的数据。
但是如果您在方法本身中将计数声明为变量,那将是安全的。因为每次调用此方法都会分配一个新的计数变量,该变量将在方法调用结束后销毁。
您可以为每个请求创建新的服务,这将是安全的,但是,这将是系统的开销。
我建议你做一个单身人士。如果出于某种原因想要替换 SOAPService 的实现,工厂是合适的。因此,单例实现将是:
public final class SoapService {
public static final String END_POINT = "endpoint";
private final Map<String, InternalSOAPService> map = Collections.synchronizedMap(new HashMap());
// All user services int the box
private final Map<String, InternalSOAPService> unmodifiableMap = Collections.unmodifiableMap(someServiceMapInitMethod());
private static SoapService ourInstance = new SoapService();
private SoapService() {
}
// All user services int the box. No delays.
public static InternalSOAPService getServiceFromTheBox(final String custId) {
return ourInstance.unmodifiableMap.get(custId);
}
public static InternalSOAPService getService(final String custId) {
InternalSOAPService service = ourInstance.map.get(custId);
if (service == null) {
synchronized (SoapService.class) {
service = ourInstance.new InternalSOAPService(custId);
if (service == null) { // Double Checked Locking
service = ourInstance.new InternalSOAPService(custId);
}
ourInstance.map.put(custId, service);
}
}
return service;
}
public class InternalSOAPService {
private final String custId;
private final SOAPStub stub;
public InternalSOAPService(final String custId) {
this.custId = custId;
this.stub = new SOAPStub(END_POINT); // Here could be Factory based on custId or END_POINT
}
public Object getProductList(){
return stub.getProductList();
}
public Object getProductInfo(final String productId){
return stub.getProductList(productId);
}
}
}
您的 HashMap 不是线程安全的,您所走的整个道路是否高效似乎值得怀疑。您的线程可能将所有时间都花在阻塞对共享池的访问上,并且随着负载的增加,争用会变得更糟。尽可能使您的线程彼此独立,即使这会使它们使用更多内存。
通常为数据库连接等资源密集型对象保留池。参见 this question about object pooling。缓存可能对您有帮助,请查看像 ehcache 这样的缓存提供程序。滚动你自己的缓存比你想象的要麻烦。
It is advisable to create new object wisely and use the same Object
instance instead of creating a new one.
建议如果性能提升值得引入的复杂性。根据我的经验,在现代面向对象平台上很少出现这种情况。
您对多线程环境中会发生什么感到困惑就足以表明权衡可能不会那么有利可图。
我的看法:在您证明样本 #1 更新的对象数量会严重损害产品性能之前,样本 #2 是过早的优化。
建议明智地创建新对象并使用相同的对象实例而不是创建新对象。我对决定在以下场景中创建对象的解决方案不是很有信心。有一个 SOAP 服务 class,它有几个方法负责多个 顾客。请看下面的模板,
Public class SOAPService {
public Object getProductList(String CustId, String endPoint){
SOAPStub stub = new SOAPStub(endPoint);
Object productList = stub.getProductList();
return productList;
}
public Object getProductInfo(String CustId, String productId, String endPoint){
SOAPStub stub = new SOAPStub(endPoint);
Object productInfo = stub.getProductList(productId);
return productInfo;
}
}
现在我介绍了一个工厂方法来为每个客户创建对象并将其放入映射中,但是当单个客户的多个线程访问该服务时我感到困惑class。对象的行为不会像单例或可能导致任何死锁问题或使线程等待吗?请赐教。
Public class SOAPService {
private Map<String, SOAPStub> map = new HashMap<String, SOAPStub>();
public SOAPStub getSOAPObject(String CustId, String endPoint){
if(map.containsKey(CustId))
return map.get(CustId);
else{
SOAPStub stub = new SOAPStub(endPoint);
map.put(custId, stub);
return stub;
}
}
public Object getProductList(String CustId, String endPoint){
SOAPStub stub = getSOAPObject(CustId, endPoint);
Object productList = stub.getProductList();
return productList;
}
public Object getProductInfo(String CustId, String productId, String endPoint){
SOAPStub stub = getSOAPObject(CustId, endPoint);
Object productInfo = stub.getProductList(productId);
return productInfo;
}
}
您的第一个代码示例看起来很安全,但是..
是的,只有在 class 服务方法在 read/write 模式下使用 class 变量时,才会为服务 class "MAY" 创建问题.
例如让我们采用以下服务方法
private int count = 0; //class variable
public Response service(Request r )
{
count = r.getSomeVariable();
...
response.setParameter(count);
}
以上方法可能不安全,因为每个请求都有自己的线程,并且还共享公共变量 "count" 当同时调用两个请求时,它们可能会覆盖彼此的数据。
但是如果您在方法本身中将计数声明为变量,那将是安全的。因为每次调用此方法都会分配一个新的计数变量,该变量将在方法调用结束后销毁。
您可以为每个请求创建新的服务,这将是安全的,但是,这将是系统的开销。
我建议你做一个单身人士。如果出于某种原因想要替换 SOAPService 的实现,工厂是合适的。因此,单例实现将是:
public final class SoapService {
public static final String END_POINT = "endpoint";
private final Map<String, InternalSOAPService> map = Collections.synchronizedMap(new HashMap());
// All user services int the box
private final Map<String, InternalSOAPService> unmodifiableMap = Collections.unmodifiableMap(someServiceMapInitMethod());
private static SoapService ourInstance = new SoapService();
private SoapService() {
}
// All user services int the box. No delays.
public static InternalSOAPService getServiceFromTheBox(final String custId) {
return ourInstance.unmodifiableMap.get(custId);
}
public static InternalSOAPService getService(final String custId) {
InternalSOAPService service = ourInstance.map.get(custId);
if (service == null) {
synchronized (SoapService.class) {
service = ourInstance.new InternalSOAPService(custId);
if (service == null) { // Double Checked Locking
service = ourInstance.new InternalSOAPService(custId);
}
ourInstance.map.put(custId, service);
}
}
return service;
}
public class InternalSOAPService {
private final String custId;
private final SOAPStub stub;
public InternalSOAPService(final String custId) {
this.custId = custId;
this.stub = new SOAPStub(END_POINT); // Here could be Factory based on custId or END_POINT
}
public Object getProductList(){
return stub.getProductList();
}
public Object getProductInfo(final String productId){
return stub.getProductList(productId);
}
}
}
您的 HashMap 不是线程安全的,您所走的整个道路是否高效似乎值得怀疑。您的线程可能将所有时间都花在阻塞对共享池的访问上,并且随着负载的增加,争用会变得更糟。尽可能使您的线程彼此独立,即使这会使它们使用更多内存。
通常为数据库连接等资源密集型对象保留池。参见 this question about object pooling。缓存可能对您有帮助,请查看像 ehcache 这样的缓存提供程序。滚动你自己的缓存比你想象的要麻烦。
It is advisable to create new object wisely and use the same Object instance instead of creating a new one.
建议如果性能提升值得引入的复杂性。根据我的经验,在现代面向对象平台上很少出现这种情况。
您对多线程环境中会发生什么感到困惑就足以表明权衡可能不会那么有利可图。
我的看法:在您证明样本 #1 更新的对象数量会严重损害产品性能之前,样本 #2 是过早的优化。