Hvad er brugen af metoden Collections.synchronizedList()? Det ser ikke ud til at synkronisere listen
En synkroniseret liste synkroniserer kun metoder på denne liste.
Det betyder, at en tråd ikke vil være i stand til at ændre listen, mens en anden tråd i øjeblikket kører en metode fra denne liste. Objektet er låst under behandlingsmetoden.
Som et eksempel, lad os sige, at to tråde kører addAll
på din liste med 2 forskellige lister (A=A1,A2,A3
og B=B1,B2,B3
) som parameter.
-
Da metoden er synkroniseret, kan du være sikker på, at disse lister ikke vil blive flettet tilfældigt som
A1,B1,A2,A3,B2,B3
-
Du bestemmer ikke, hvornår en tråd overdrager processen til den anden tråd. Hvert metodekald skal køre fuldt ud og vende tilbage, før det andet kan køre. Så du kan enten få
A1,A2,A3,B1,B2,B3
ellerB1,B2,B3,A1,A2,A3
(Da vi ikke ved, hvilket trådopkald der kører først).
I dit første stykke kode kører begge tråde på samme tid. Og begge prøver at add
et element til listen. Du har ingen måde at blokere en tråd på, undtagen synkroniseringen på add
metode, så intet forhindrer tråd 1 i at køre flere add
operation, før processen overdrages til tråd 2. Så dit output er helt normalt.
I dit andet stykke kode (det ukommenterede) angiver du tydeligt, at en tråd helt låser listen fra den anden tråd, før du starter løkken. Derfor skal du sikre dig, at en af dine tråde kører hele løkken, før den anden kan få adgang til listen.
Collections.synchronizedList()
vil synkronisere alle adgange til den sikkerhedskopierede liste undtagen under iteration, som stadig skal udføres inden for en synkroniseret blok med den synkroniserede List-forekomst som objektets monitor.
Så for eksempel her er koden for add
metode
public boolean add(E e) {
synchronized (mutex) {return c.add(e);}
}
Dette garanterer seriel adgang til den sikkerhedskopierede liste, så hvis dine 2 tråde kalder add
samtidig vil den ene tråd erhverve låsen, tilføje sit element og frigive låsen, så vil den anden tråd være i stand til at erhverve låsen og tilføje sit element, derfor får du alternativt one
og two
i dit output.
Når du fjerner kommentarer til den synkroniserede blok, er koden derefter
synchronized(o) {
for(int i=0;i<100;i++){
...
}
}
I dette tilfælde tråden, der kunne erhverve låsen på o
først vil udføre hele for
sløjfe, før du frigiver låsen (undtagen hvis en undtagelse er kastet), så den anden tråd kan udføre indholdet af sin synkroniserede blok, hvilket er grunden til, at du får 100
på hinanden følgende gange one
eller two
derefter 100
fortløbende gange den anden værdi.
Dette er et sejt lille eksempel baseret på det originale eksempel og det accepterede svar for at vise hvilket formål synchronizedList
tjener.
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);
}
}
Den originale list
ender med at have udefineret adfærd med resultater som:
[one, one, one] // wrong!
[one, one, one, null, null, null] // wrong!
[two, two, two] // wrong!
[one, one, one, two, two, two] // correct
Mens den synkroniserede synList
har en synkroniseret addAll
metode og giver altid et af de to rigtige resultater:
[one, one, one, two, two, two] // correct
[two, two, two, one, one, one] // correct