Java 设计模式:工厂与单例?在多线程的情况下



建议明智地创建新对象并使用相同的对象实例,而不是创建新对象。我对在以下情况下决定创建对象的解决方案不是很有信心。有一个 SOAP 服务类,它有几个方法负责多个客户。请参阅下面的模板,

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;
            }
    }

现在我介绍了一种工厂方法来为每个客户创建 Object 并将其放在地图中,但是当单个客户的多个线程访问服务类时,我很困惑。 对象的行为不会像单例 OR 一样可能导致任何死锁问题或使线程等待吗?请启发我。

    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;
        }
    }

你的HashMap不是线程安全的,你走的整个道路是否会富有成效似乎值得怀疑。线程可能会将所有时间都花在阻止对共享池的访问上,并且随着负载的增加,争用将变得更糟。尽可能使线程彼此独立,即使这会使它们使用更多内存。

通常,为资源密集型对象(如数据库连接)保留池。请参阅有关对象池的问题。缓存可能对您有所帮助,请查看像 ehcache 这样的缓存提供程序。滚动自己的缓存比您想象的更麻烦。

你的第一个代码示例看起来很安全,但是。

是的,仅当您具有服务方法在读/写模式下使用的类变量时,服务类"MAY"的单例对象才会产生问题。

例如,让我们采用以下服务方法

private int count = 0; //class variable 
public Response service(Request r ) 
{
count = r.getSomeVariable();
...
response.setParameter(count);
}

上述方法可能不安全,因为每个请求都有自己的线程,并且当同时调用两个请求时,它们可能会覆盖彼此的数据,并且还共享公共变量"count"。

但是,如果您在方法本身中将count声明为变量,这将是安全的。 因为每次调用此方法时都会分配一个新的计数变量,该变量将在方法调用结束后销毁。

您可以为每个请求创建新服务,这将是安全的,但是,这将是系统中的开销。

建议明智地创建新对象并使用相同的对象 实例,而不是创建一个新实例。

如果性能提升值得引起的复杂性,则建议这样做。根据我的经验,在现代面向对象的平台上很少出现这种情况。

您对多线程环境中会发生什么感到困惑足以表明权衡可能没有那么有利可图。

我的看法:在你证明样本 #1 更新的对象数量会不可接受地损害生产性能之前,样本 #2 是过早的优化。

我建议你作为签名。如果由于某种原因想要替换 SOAPS服务的实现,工厂是合适的。因此,签名实现可以是:

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);
        }
    }
}

最新更新