2016-02-05 79 views
1

我有一个工具类有一个静态方法来修改输入数组列表的值。这个静态方法由调用者调用。调用者用于处理Web服务请求。对于每个请求(每个线程),调用者创建一个新的ArrayList并调用静态方法。这是静态方法线程安全或需要同步

public class Caller{ 

    public void callingMethod(){ 

    //Get Cloned criteria clones a preset search criteria that has place holders for values and returns a new ArrayList of the original criteria. Not included code for the clone 
    ArrayList<Properties> clonedCriteria = getClonedCriteria(); 

    CriteriaUpdater.update(clonedCriteria , "key1", "old_value1", "key1_new_value"); 
    CriteriaUpdater.update(clonedCriteria , "key2", "old_value2", "key2_new_value"); 

    //do something after the this call with the modified criteria arraylist 
    } 

} 

public class CriteriaUpdater 
{ 
    //updates the criteria, in the form of array of property objects, by replacing the token with the new value passed in 
    public static void update(ArrayList<Properties> criteria, String key, String token, String newValue) 
    { 
     for (Properties sc: criteria) 
     { 
      String oldValue = sc.getProperty(key); 
      if ((oldValue != null) && (oldValue.equals(token))) 
       sc.setProperty(key, newValue); 
     } 
    } 
} 

这就是标准如何被克隆:

public synchronized static ArrayList<Properties> cloneSearchCriteria(ArrayList<Properties> criteria) { 
    if (criteria == null) return null; 
    ArrayList<Properties> criteriaClone = new ArrayList<Properties>(); 
    for (Properties sc : criteria) { 
    Properties clone = new Properties(); 
    Enumeration propertyNames = sc.propertyNames(); 
    while (propertyNames.hasMoreElements()) { 
     String key = (String) propertyNames.nextElement(); 
     clone.put(key, (String) sc.get(key)); 
    } 
    criteriaClone.add(clone); 
    } 
    return criteriaClone; 
} 

鉴于上述定义,通过不同步的静态方法,将它仍然能够正确处理并发方法调用。我的理解是我必须同步这种并发的方法,但想确认。 我知道每个线程都有自己的堆栈,但是对于静态方法来说,它对于所有线程都很常见 - 所以在这种情况下,如果我们不同步,它不会导致问题? 欣赏建议和任何更正。

谢谢

+0

“预设搜索条件”是否被另一个线程更改过?在“线程之间共享任何数据做任何事情之后都会做些什么? – rgettman

+0

'getClonedCriteria'返回一个同步副本,或只是一个参考副本? – Tersosauros

+0

预设标准不会更改。 公共同步静态的ArrayList cloneSearchCriteria(ArrayList的条件){ \t \t如果(标准== NULL) \t \t \t返回NULL; \t \t ArrayList criteriaClone = new ArrayList (); (Properties sc:criteria) \t \t(012) \t \t \t Enumeration propertyNames = sc.propertyNames(); \t \t \t而(propertyNames.hasMoreElements()){ \t \t \t \t String键=(字符串)propertyNames.nextElement(); \t \t \t \t clone.put(key,(String)sc.get(key)); \t \t \t} \t \t \t criteriaClone.add(克隆); \t \t} \t \t return criteriaClone; \t} – sbelvadi

回答

0

这一切都取决于你的getClonedCriteria()方法。这是访问共享状态的方法。

您正在创建条件的“深层副本”,以便每个副本都独立于原始副本和相互之间。

但是还有一个更微妙的问题,那就是不管初始化是否在原型标准上执行,都必须发生之前的任何线程读取标准以克隆它。否则,克隆线程可能会读取数据结构的未初始化版本。

实现此目标的一种方法是在静态初始化器中初始化原型标准并将其分配给类成员变量。另一个是初始化标准,然后将其分配给volatile变量。或者,您可以初始化并将原型(以任意顺序)分配给​​块内的普通类或实例成员变量(或使用Lock),然后从同一个锁上同步的另一个块读取该变量。

+0

在应用程序启动时,将原始搜索条件弹簧注入调用者(在春天配置为单例)。在春天的应用程序上下文文件中,这个原始搜索条件是使用键值对进行初始化的。调用者然后调用实用程序类的克隆方法,以在处理Web服务请求时返回原始搜索条件的克隆(因为我想确保原始条件未被更改)。不知道这是否与你的建议相似 – sbelvadi

+0

@sbelvadi因此,'getClonedCriteria()'是一个类似于'return cloneSearchCriteria(original)'的实例方法吗?在这种情况下,'original'(或者你命名的)是由Spring注入设置的? – erickson

+0

original未被声明为final或volatile,但在Spring应用程序上下文中定义,并且Dependency在应用程序启动时通过弹簧应用程序上下文注入到调用者中。实际上,在调用者中,我使用getter获取原始搜索条件,并且克隆的目的是对副本进行任何修改,而不是对原始进行修改。鉴于此,我必须使 公共静态无效更新(ArrayList 标准,字符串键,字符串标记,字符串newValue) 同步? – sbelvadi

1

您遇到了一个竞赛条件问题。至少底层的Properties数据结构永远不会被破坏,但可能会有不正确的值。特别是,本节中可以包含任意数量的线程,这意味着最终值可以是任何线程中的任何线程。

 String oldValue = sc.getProperty(key); 
     if ((oldValue != null) && (oldValue.equals(token))) 
      sc.setProperty(key, newValue); 

我假设你的列表不会被改变,但如果是,你必须有​​。你可以锁定课程,但锁定你正在改变的集合可能是更好的选择。

+0

不知道我是否理解正确。 public synchronized static ArrayList cloneSearchCriteria(ArrayList criteria){ \t if(criteria == null) \t return null; \t ArrayList criteriaClone = new ArrayList (); \t for(Properties sc:criteria){ \t \t属性clone = new Properties();枚举propertyNames = sc.propertyNames(); \t \t而(propertyNames.hasMoreElements()){ \t \t \t String键=(字符串)propertyNames.nextElement(); \t \t \t clone.put(key,(String)sc.get(key)); \t \t} \t \t \t criteriaClone.add(clone); \t} \t return criteriaClone; } – sbelvadi

+0

继续:您是否在更新()中说,我必须有一个 同步(条件){ } } – sbelvadi

+0

后续编辑显示为显示数据的深层副本。你的答案是否仍然适用? – erickson

0

你是正确的,因为每个线程都有自己的堆栈,所以当它调用update()时,每个线程都有自己的局部变量和方法参数副本。它运行时会将这些局部变量和方法参数保存到它的堆栈中。

但是,方法参数criteria是对可变对象的引用,它将存储在Java对象所在的堆上。如果线程可以在同一个ArrayList上调用update(),或者ArrayList中包含的元素可以包含在由不同线程传递到update()的不同线程中的多个ArrayList中,则可能会发生同步错误。

+0

预设数组列表和此克隆的克隆的标准对调用方法而言是本地的。因此,考虑到这一点,传递给update()的标准会被认为是本地的,而不是共享的,因为我会传递一个引用。 – sbelvadi