同步代码块

use*_*321 2 java multithreading android thread-safety

为什么人们只为一行代码"同步"?什么是"同步"?

public final void addListener(Listener listener) {
  synchronized (listeners) {
    listeners.add(listener);
  }
}
Run Code Online (Sandbox Code Playgroud)

编辑:谢谢大家.来自所有人的非常好的答案!

Mic*_*sen 7

synchronized就其本身而言,如果多个线程同时尝试运行这段代码,则在任何给定时间内只允许其中一个线程进入块内.synchronized (listeners)使用listeners作为锁定标识,这意味着该限制适用于该同步于该变量的所有块-如果一个线程内的那些块中的一个,没有其他的线程可以进入它们.

即使块中只有一个函数调用,这仍然有意义:该函数由许多其他指令组成,并且控件可以切换到不同的线程,而第一个在该函数的中间.如果该函数不是线程安全的,则可能导致问题,例如数据被覆盖.

在这种特殊情况下,函数调用包括向集合中添加值listeners.虽然创建一个线程安全的集合并非不可能,但大多数集合对于多个编写者来说并不是线程安全的.因此,为了确保收集不会搞砸,synchronized需要.

编辑:举例说明事情可能会搞砸,假设这个简化的实现add,其中lengthitems数组中元素的数量:

public void Add(T item) {
  items[length++] = item;
}
Run Code Online (Sandbox Code Playgroud)

length++一点不是原子的; 它由一个读取,一个增量和一个写入组成,线程可以在任何一个之后被中断.所以,让我们重写一下,看看到底发生了什么:

public void Add(T item) {
  int temp = length;
  length = length + 1;
  items[temp] = item;
}
Run Code Online (Sandbox Code Playgroud)

现在假设两个线程T1和T2同时输入Add.这是一组可能的事件:

T1: int temp = length;
T2: int temp = length;
T2: length = length + 1;
T2: items[temp] = item;
T1: length = length + 1;
T1: items[temp] = item;
Run Code Online (Sandbox Code Playgroud)

问题是两个线程都使用相同的值temp,因此最后一个线程将最终覆盖第一个放在那里的项目; 并且最后有一个未分配的项目.

如果length表示要使用的下一个索引,它也没有帮助,所以我们可以使用preincrement:

public void Add(T item) {
  items[++length] = item;
}
Run Code Online (Sandbox Code Playgroud)

我们再次重写:

public void Add(T item) {
  length = length + 1;
  items[length] = item;
}
Run Code Online (Sandbox Code Playgroud)

现在这是一个可能的事件序列:

T1: length = length + 1;
T2: length = length + 1;
T2: items[length] = item;
T1: items[length] = item;
Run Code Online (Sandbox Code Playgroud)

再次,最后一个线程最终覆盖第一个,但现在未分配的项目是倒数第二个项目.

  • 不只是_this_代码片段,_any_代码在`listeners`上同步.锁定在对象上,而不是代码. (2认同)