ArrayList,Threads和synchronize - 如何同步完全适用于多个线程

时间:2011-05-27 09:35:51

标签: java thread-safety arraylist synchronized

再一次关于ArrayList和同步的问题。

我想知道这个片段的确切作用:

ArrayList<ObjectX> list = ....;

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

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

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

这就是我想要实现的目标。 会有效吗?: - )

所以,如果是的话,我想知道该片段到底做了什么。是否阻止两个线程同时访问此完全代码?那么代码只能同时用于一个线程

或者它是否一直锁定列表本身,对于此时尝试访问列表的应用程序中的任何线程 - 任何地方? (我的代码中没有其他add(),但是很多得到(),这就是为什么我想知道其他线程是否可以访问列表并且在另一个线程正在访问上面的代码时仍然获取元素。)

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

那就是我想知道的。我试图标记我的问题,以便更容易回答它们。谢谢你的帮忙! : - )

[编辑]感谢您的所有答案,几乎所有人都说了同样的答案!我觉得现在很清楚! : - )

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

正确?我希望如此: - )

6 个答案:

答案 0 :(得分:5)

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

答案 1 :(得分:3)

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

synchronized (list) {
    // block A
}

synchronized (list) {
    // block B
}

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

答案 2 :(得分:2)

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

您对用户主体的假设也是正确的。如果您的数组列表是每个用户(主体)一个,那么只有使用该用户(主体)执行的线程才必须在该特定数组列表上同步。

答案 3 :(得分:1)

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

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

每个对象都可以用作锁。

答案 4 :(得分: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,你的同步没什么区别,但是拥有单独的锁对象更安全。

答案 5 :(得分:0)

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

但是如果你经常写作,那就会产生很大的开销。