Java Collections.synchronizedList() 方法有什么用?似乎没有同步列表
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/40930861/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
What is the use of Collections.synchronizedList() method? It doesn't seem to synchronize the list
提问by Fullstack Guy
I am trying to add String
values to an ArrayList
using two threads. What I want is that while one thread is adding the values the other thread should not interfere so I have used the Collections.synchronizedList
method. But it appears that if I don't explicitly synchronize on an object the adding is done in an unsynchronized way.
我正在尝试使用两个线程String
向一个添加值ArrayList
。我想要的是,当一个线程添加值时,另一个线程不应该干扰,所以我使用了该Collections.synchronizedList
方法。但似乎如果我没有显式同步对象,则添加是以不同步的方式完成的。
Without explicit synchronized block:
没有显式同步块:
public class SynTest {
public static void main(String []args){
final List<String> list=new ArrayList<String>();
final List<String> synList=Collections.synchronizedList(list);
final Object o=new Object();
Thread tOne=new Thread(new Runnable(){
@Override
public void run() {
//synchronized(o){
for(int i=0;i<100;i++){
System.out.println(synList.add("add one"+i)+ " one");
}
//}
}
});
Thread tTwo=new Thread(new Runnable(){
@Override
public void run() {
//synchronized(o){
for(int i=0;i<100;i++){
System.out.println(synList.add("add two"+i)+" two");
}
//}
}
});
tOne.start();
tTwo.start();
}
}
The output that I got is:
我得到的输出是:
true one
true two
true one
true two
true one
true two
true two
true one
true one
true one...
With the explicit synchronized block uncommented I'm stopping the interference from the other thread while adding. Once the thread has acquired the lock it is executing until it is finished.
随着显式同步块取消注释,我在添加时停止来自其他线程的干扰。一旦线程获得了锁,它就会一直执行直到完成。
sample output after uncommenting the synchronized block:
取消注释同步块后的示例输出:
true one
true one
true one
true one
true one
true one
true one
true one...
So why is the Collections.synchronizedList()
not doing the synchronization?
那么为什么Collections.synchronizedList()
不进行同步呢?
采纳答案by jhamon
A synchronized list only synchronizes methods of this list.
同步列表仅同步此列表的方法。
It means a thread won't be able to modify the list while another thread is currently running a method from this list. The object is locked while processing method.
这意味着当另一个线程当前正在运行此列表中的方法时,一个线程将无法修改该列表。对象在处理方法时被锁定。
As an example, Let's say two threads run addAll
on your list, with 2 different lists (A=A1,A2,A3
and B=B1,B2,B3
) as parameter.
例如,假设有两个线程addAll
在您的列表上运行,并使用 2 个不同的列表 (A=A1,A2,A3
和B=B1,B2,B3
) 作为参数。
As the method is synchronized, you can be sure those lists won't be merged randomly like
A1,B1,A2,A3,B2,B3
You don't decide when a thread handover the process to the other thread. Each method call has to fully run and return before the other one could run. So you can either get
A1,A2,A3,B1,B2,B3
orB1,B2,B3,A1,A2,A3
(As we don't know which thread call will run first).
由于该方法是同步的,您可以确保这些列表不会像
A1,B1,A2,A3,B2,B3
您无法决定一个线程何时将进程移交给另一个线程。每个方法调用都必须完全运行并在另一个方法调用之前返回。所以你可以得到
A1,A2,A3,B1,B2,B3
或B1,B2,B3,A1,A2,A3
(因为我们不知道哪个线程调用将首先运行)。
In your first piece of code, both threads runs on the same time. And both try to add
an element to the list. You don't have any way to block one thread except the synchronization on the add
method so nothing prevent thread 1 from running multiple add
operation before handing over the process to thread 2. So your output is perfectly normal.
在您的第一段代码中,两个线程同时运行。并且都尝试将add
元素添加到列表中。除了add
方法上的同步之外,您没有任何方法可以阻止一个线程,因此没有什么可以阻止线程 1add
在将进程移交给线程 2 之前运行多个操作。所以您的输出是完全正常的。
In your second piece of code (the uncommented one), you clearly state that a thread completely lock the list from the other thread before starting the loop. Hence, you make sure one of your thread will run the full loop before the other one could access the list.
在您的第二段代码(未注释的)中,您清楚地说明一个线程在开始循环之前完全锁定了另一个线程的列表。因此,您要确保您的一个线程在另一个线程可以访问列表之前运行完整循环。
回答by bsiamionau
Observable behavior is absolutely correct - synchronized
approach you demonstrate in code sample does not the same as synchronizedList
. In first case you synchronize the whole for-statement, so only one thread will execute it simultaniously. In the second case you synchromize collection methods itself - that's what synchronizedList
stands for. So be sure that add
method is synchronized - but not the for
method!
Observable 行为是绝对正确的 -synchronized
您在代码示例中演示的方法与synchronizedList
. 在第一种情况下,您同步整个 for 语句,因此只有一个线程会同时执行它。在第二种情况下,您同步收集方法本身 - 这就是synchronizedList
代表。所以要确保add
方法是同步的——但不是for
方法!
回答by Nicolas Filotto
Collections.synchronizedList()
will synchronize all the accesses to the backed list except while iterating which still needs to be done within a synchronized block with the synchronized List instance as object's monitor.
Collections.synchronizedList()
将同步对支持列表的所有访问,除了迭代时,这仍然需要在同步块内完成,同步列表实例作为对象的监视器。
So for example here is the code of the add
method
所以例如这里是add
方法的代码
public boolean add(E e) {
synchronized (mutex) {return c.add(e);}
}
This guarantees serial access to the backed list, so if your 2 threads call add
at the same time, one thread will acquire the lock, add its element and release the lock then the second thread will be able to acquire the lock and add its element that is why you get alternatively one
and two
in your output.
这保证了对支持列表的串行访问,因此如果您的 2 个线程同时调用add
,一个线程将获取锁,添加其元素并释放锁,然后第二个线程将能够获取锁并添加其元素就是为什么你或者one
和two
你的输出。
When you uncomment the synchronized block, the code is then
当您取消对同步块的注释时,代码将变为
synchronized(o) {
for(int i=0;i<100;i++){
...
}
}
In this case the thread that could acquire the lock on o
first will execute the entirefor
loop before releasing the lock (except if an exception is thrown), allowing the other thread to execute the content of its synchronized block, which is why you get 100
consecutive times one
or two
then 100
consecutive times the other value.
在这种情况下,可以先获取锁的线程o
将在释放锁之前执行整个for
循环(除非抛出异常),从而允许另一个线程执行其同步块的内容,这就是您获得100
连续时间的原因one
或two
然后100
连续乘以另一个值。
回答by e2a
According to previous answers you need to synchronize the synList
from access thread tOne
and tTwo
. In this case, you can use the monitor pattern to provides a security access - in order for threads.
根据之前的答案,您需要同步synList
from 访问线程tOne
和tTwo
. 在这种情况下,您可以使用监视器模式来提供安全访问 - 为了线程。
Below I adapted your code to be share with others that have same problems. In this code I used only the synList
to control access in synchronized way. Notice that it is not necessary create other object to ensure the order access from synList.
To complement this question, see book Java Concurrency in Practice jcipchapter 4 that talks about monitor design patterns that is inspired by Hoare's work
下面我调整了您的代码以与其他有相同问题的人分享。在这段代码中,我只使用了synList
以同步方式控制访问。注意,没有必要创建其他对象来确保从synList 的顺序访问。为了补充这个问题,请参阅 Java Concurrency in Practice jcip第 4 章,该书讨论了受 Hoare 工作启发的监视器设计模式
public class SynTest {
public static void main(String []args){
final List<String> synList= Collections.synchronizedList(new ArrayList<>());
Thread tOne=new Thread(() -> {
synchronized (synList) {
for (int i = 0; i < 100; i++) {
System.out.println(synList.add("add one" + i) + " one");
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
});
Thread tTwo=new Thread(()->{
synchronized (synList) {
for(int i=0;i<100;i++){
System.out.println(synList.add("add two"+i)+" two");
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
});
tOne.start();
tTwo.start();
}
}
}
回答by Mo Beigi
This is a cool little example based on the original example and accepted answer to show what purpose synchronizedList
serves.
这是一个基于原始示例的很酷的小示例,并接受了用于说明目的的答案synchronizedList
。
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
public class SynTest {
public static void main(String []args) throws InterruptedException
{
final List<String> list = new ArrayList<>();
final List<String> synList = Collections.synchronizedList(new ArrayList<>());
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
list.addAll(Arrays.asList("one", "one", "one"));
synList.addAll(Arrays.asList("one", "one", "one"));
}
});
Thread t2 = new Thread(new Runnable() {
@Override
public void run() {
list.addAll(Arrays.asList("two", "two", "two"));
synList.addAll(Arrays.asList("two", "two", "two"));
}
});
t1.start();
t2.start();
Thread.sleep(1000);
System.out.println(list);
System.out.println(synList);
}
}
The original list
ends up having undefined behaviour with results such as:
原始list
最终具有未定义的行为,结果如下:
[one, one, one] // wrong!
[one, one, one, null, null, null] // wrong!
[two, two, two] // wrong!
[one, one, one, two, two, two] // correct
While the synchronized synList
has a synchronised addAll
method and always produces one of the two correct results:
而 synchronizedsynList
有一个 synchronizedaddAll
方法并且总是产生两个正确结果之一:
[one, one, one, two, two, two] // correct
[two, two, two, one, one, one] // correct