Java >> Java tutorial >  >> Tag >> synchronized

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 eller B1,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

Java tag