2011-05-27 149 views
3

再次提出有关ArrayList和同步的问题。ArrayList,线程和同步 - 如何准确同步多个线程

我只是想知道这是什么片断究竟:

ArrayList<ObjectX> list = ....; 

synchronized (list) { 
    if (list.contains(objectxy) == false) { 
     list.add(objectxy); 
    } 
} 

Ive得到了充满ObjectXs一个ArrayList中。我想添加一个元素到列表中,但只有当列表不包含相同的元素。我之前(在另一种方法中)检查列表是否包含对象 - 结果是否定的。但是有可能两个线程同时认为结果是否定的,并且他们都尝试添加objectxy。 (还有其他一些必须在中间做的事情,这就是为什么我不能同步整个过程)

所以,在过程之后,当现在的线程来到上面的片段,我想防止这两个都添加该对象到列表中。所以我想当我同步访问列表时,只有一个线程可以检查它是否包含对象,然后添加它。在它之后,第二个线程可以访问列表,查看该对象已经在其中并且不再添加它。

那就是我想要达到的。 它会工作吗? :-)

所以,如果是的话,我想知道片段到底是什么。是否阻止两个线程同时访问此确切的代码?这样代码只能同时用于一个线程

或者它锁定列表本身整个时间,在应用程序的任何线程是谁在这个时候尝试访问列表 - 随时随地? (我没有其他的add()在我的代码中,但许多gets(),这就是为什么Id喜欢知​​道其他线程是否可以访问列表,并仍然获取元素,而另一个线程访问上面的代码)。

ArrayList本身是一个使用应用程序与主体连接的成员变量。多个不同的线程同时访问上面的代码是正确的如果它们不是从相同的主体发送的,那么正确的?

所以,这就是Id想知道的。我试图标记我的问题,以便更容易地回答它们。感谢您的帮助! :-)

[编辑]谢谢你所有的答案,这几乎所有的说法都一样!我认为现在很清楚! :-)

  • 同步代码块只能由其中一个主体线程访问。 (其他校长的线索与特殊校长无关)。该列表本身可以随时从其他线程访问 - 只要对它的访问不与同步块同步即可。如果是这样,线程必须等待,直到它可以访问列表(这意味着没有其他线程同时在同步块中)

是否正确?我希望如此:-)

回答

3

同步块保证只有一个线程可以执行该代码块,或者同时在同一对象(即列表)上同步的任何其他代码块。例如,如果你有

synchronized (list) { 
    // block A 
} 

synchronized (list) { 
    // block B 
} 

,那么如果一个线程正在执行块A,没有其他的线程可以被执行嵌段A或嵌段B,因为它们都在同一对象上同步。但列表本身并未锁定。另一个线程可能访问列表。

5

你几乎拥有它。​​可防止锁定同一list对象的其他线程同时运行其代码块。它不会锁定list对象本身。其他线程仍然可以访问它,如果他们不同步在同一个对象上。

2

是的,一次只有一个线程可以访问该代码块。所有其他线程将等待,直到首先到达的线程将完成代码块的执行。

另外,您对用户主体的假设是正确的。如果您的数组列表是每个用户(主体)的数组,则只有与该用户(主体)一起执行的线程才需要在该特定数组列表上同步。

1

除了同意像其他答案一样建议它只会阻塞同一块代码。 我认为你有的关键困惑,并且大多数人都有关于锁定在同步(锁定)。你使用列表本身作为你的情况下的锁。但是,使用一个对象作为锁,并且如果对象中的代码将被阻止,则完全不相关。实际上,只要它是同一个对象,就可以使用任何对象作为锁。这意味着如果你有一个名为另一个成员变量,下面的代码将运行基本相同:

synchronized (foo) { 
    if (list.contains(objectxy) == false) { 
     list.add(objectxy); 
    } 
} 

每个对象都可以被用作一个锁。

0

只要所有线程在做任何事情之前在对象上同步,它就会工作。常见做法是隐藏其他人的列表并给予只读副本。

public class ThreadSafeList { 

    private ArrayList<String> list = new ArrayList<String>(); 


    public synchronized void addUnique(String s) { 
     if (!list.contains(s)) { 
      list.add(s); 
     } 
    } 


    public synchronized List<String> getList() { 
     return Collections.unmodifiableList((new ArrayList<String>(list))); 
    } 
} 

通过封装来保证同步。

同步方法类似于

public void addUnique(String s) 
    synchronized(this){ 
    list.add(s); 
    } 

和Java有你sychronize没有什么区别,但它是安全有单独的锁定对象。

0

您也可以使用Vector(同步列表)或其他同步集合。 如果您正在进行多次读取但写入较少,则可以使用CopyOnWriteArrayList

但是,如果你经常在写作,它会产生很多开销。

+0

感谢您的回答。我不使用synchronizedList,因为我对它有非常少的操作需要同步 - 这就是为什么我认为这是没有必要的。由于CopyOnWriteArrayList也是线程安全的,我认为这对我的应用程序来说也太多了 - 我实际上并不需要在每个点都进行同步。 :-) – nano7 2011-05-27 10:16:56

+0

这种方法要非常小心:您必须确保*每个*列表访问都不会与任何其他访问重叠。例如,如果有一个线程执行get()而另一个执行同步(list){add()/ set()}指令的机会,您必须*同步get()指令。 – Flavio 2011-05-27 10:27:18

+0

为什么?如果仅仅因为用户可以获得不是最新的数据(可能缺少一个添加),那么这并不重要 - 至少不适用于我的应用程序,我的意思是:-)我可以接受那个 – nano7 2011-05-27 12:01:38