Что такое использование коллекций.synchronizedList() метод? Похоже, он не синхронизирует список
Я пытаюсь добавить String
значения ArrayList
использование двух потоков. Я хочу, чтобы в то время как один поток добавляет значения, другой поток не должен вмешиваться, поэтому я использовал Collections.synchronizedList
метод. Но похоже, что если я явно не синхронизирую объект, добавление выполняется несинхронизированным способом.
без явного synchronized-блок:
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();
}
}
вывод, который я получил:
true one
true two
true one
true two
true one
true two
true two
true one
true one
true one...
с явным синхронизированным блоком uncommented я останавливаю интерференцию из другого потока при добавлении. Как только поток приобрел блокировку, он выполняется до тех пор, пока он не будет завершен.
пример вывода после разблокировки синхронизированного блока:
true one
true one
true one
true one
true one
true one
true one
true one...
почему Collections.synchronizedList()
не делать синхронизацию?
3 ответов
синхронизированный список синхронизирует только методы этого списка.
это означает, что поток не сможет изменить список, пока другой поток в настоящее время выполняет метод из этого списка. Объект блокируется при обработке методом.
в качестве примера, скажем, два потока run addAll
в вашем списке, с 2 различными списками (A=A1,A2,A3
, B=B1,B2,B3
) в качестве параметра.
как метод synchronized, вы можете быть уверены, что эти списки не будут объединены случайным образом, как
A1,B1,A2,A3,B2,B3
вы не решаете, когда поток передает процесс другому потоку, чтобы вы могли либо получить
A1,A2,A3,B1,B2,B3
илиB1,B2,B3,A1,A2,A3
.
в вашем первом фрагменте кода оба потока выполняются одновременно. И оба пытаются add
элемент списка. У вас нет никакого способа заблокировать один поток, кроме синхронизации на add
метод, поэтому ничто не мешает потоку 1 запускать несколько add
операции перед передачей процесса в поток 2. Таким образом, ваш выход совершенно нормальный.
во втором фрагменте кода (незафиксированном) вы четко указываете, что поток полностью блокирует список из другого потока перед запуском цикла. Следовательно, вы убедитесь, что один из ваших потоков выполнит полный цикл, прежде чем другой сможет получить доступ к списку.
Collections.synchronizedList()
синхронизирует все обращения к резервному списку, за исключением итерации, которая все еще должна быть выполнена в синхронизированном блоке с экземпляром синхронизированного списка в качестве монитора объекта.
так, например, вот код add
метод
public boolean add(E e) {
synchronized (mutex) {return c.add(e);}
}
это гарантирует последовательный доступ к резервному списку, поэтому, если ваши 2 потока вызывают add
в то же время, один поток приобретет замок, добавит свой элемент и выпустит замок после этого второй поток сможет получить блокировку и добавить ее элемент, поэтому вы получите альтернативно one
и two
в свой выходной.
когда вы раскомментируете синхронизированный блок, код будет тогда
synchronized(o) {
for(int i=0;i<100;i++){
...
}
}
в этом случае поток может получить блокировку на o
первый выполнит весь for
цикл перед освобождением блокировки (за исключением случаев, когда возникает исключение), позволяя другому потоку выполнить содержимое его синхронизированный блок, поэтому вы получаете 100
раз подряд one
или two
затем 100
последовательные времена другое значение.
наблюдаемое поведение абсолютно правильно -synchronized
подход, который вы демонстрируете в примере кода, не совпадает с synchronizedList
. В первом случае вы синхронизируете весь оператор for -, поэтому только один поток будет выполнять его одновременно. Во втором случае вы synchromize методов сбора, сама - то чего synchronizedList
стенды для. Так что будьте уверены, что add
метод synchronized, но не for
способ!