Avatar billede bolmer Nybegynder
13. september 2006 - 11:51 Der er 25 kommentarer og
1 løsning

Problemer med Synchronized

Jeg har en ArrayList som jeg kan tilføje og hente(opdatere) elementer fra.

Da min applikation er multitrådet ønsker jeg ikke at kun én tråd kan arbejde på listen af gangen.

Fx kunne det være smart hvis alle tråde kan tilføje til listen samtidige, medmindre fx tråd 1 er ved at læse et element som tråd 2 forsøger at opdatere.

Men uanset hvad kan jeg ikke se hvordan man kan løse dette problem uden at synkronisere begge metoder. Og dette medfører netop at kun én tråd kan arbejde på liste.

Findes der ikke en løsning på dette problem?
Avatar billede thjal Nybegynder
13. september 2006 - 12:03 #1
har du hørt om semifore? Temmeligt grundliggende at vide når man arbejder med multithreadede applicationer...
Der er helt sikkert også et bestemt design pattern der løser problmet (ved dog ikke lige hvilket).
Avatar billede bolmer Nybegynder
13. september 2006 - 12:09 #2
men burder det ikke kunne løses ved at anvende det indbygget "synchronized" i java. En semafor er vel bare et tal som man tjekker før man eksekvere en blok kode, jeg er ude efter en noget mere konkret håndsrækning.
Avatar billede onkel_satan Nybegynder
13. september 2006 - 12:32 #3
Jeg er ikke verdens mester til det her thread stas, men lavede noget ligende engang. Der var ArrayListen statisk(derved class variable) og kunne derfor tilgås fra alle threads.

Hvis ikke den er statisk burge du kunne sende dit ArrayList objekt med som parameter når du opretter en ny thread.
Avatar billede bolmer Nybegynder
13. september 2006 - 12:33 #4
Det gør jeg også, problemet er hvis en tråd ønsker at læse et element, mens en anden forsøger at opdatere selvsamme element.
Avatar billede arne_v Ekspert
13. september 2006 - 15:02 #5
umiddelbart lyder det som mest rigtigt at lade de tråde der skal bruge array listen
synchronize på selve array listen

man kan godt lave en 1 write or many read lock (jeg trpr endda at jeg har noget
kode liggende)

men er det umagen værd ?

synchronized omkring små blokke er ikke så slemt på 1 CPU systemer
Avatar billede bolmer Nybegynder
13. september 2006 - 16:08 #6
Ja det er noget af det jeg er ude efter. Ved godt det ikke er 100% nødvendigt, men jeg vil gerne lære hvordan man gør det så det fungere optimalt. Hvor finder man en beskrivelse af 1 write many read lock...google gav ikke nogle særligt relevante hits
Avatar billede arne_v Ekspert
13. september 2006 - 16:43 #7
jeg lavede for et par år siden denne kode til et spørgsmål her på E:

public class MultiSemaphore {
    public final static int EXCLUSIVE = 1;
    public final static int SHARED = 2;
    private int nouser;
    private int mode;

    public MultiSemaphore() {
        nouser = 0;
    }

    public synchronized void get(int mode) {
        while ((this.mode == EXCLUSIVE || mode == EXCLUSIVE) && nouser > 0) {
            try {
                wait();
            } catch (InterruptedException e) {
                // ignore
            }
        }
        nouser++;
        this.mode = mode;
    }

    public synchronized void release() {
        nouser--;
        notifyAll();
    }

    private static void test(boolean excl) {
        long start = System.currentTimeMillis();
        MultiSemaphore sem = new MultiSemaphore();
        MyThread[] t = new MyThread[10];
        for (int i = 0; i < t.length; i++) {
            t[i] = new MyThread(sem, excl);
        }
        for (int i = 0; i < t.length; i++) {
            t[i].start();
        }
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e1) {
            // ignore
        }
        for (int i = 0; i < t.length; i++) {
            try {
                t[i].join();
            } catch (InterruptedException e) {
                // ignore
            }
        }
        long end = System.currentTimeMillis();
        System.out.println((excl ? "Exclusive" : "Shared") + " time = " + (end - start));
    }

    public static void main(String[] args) {
        test(true);
        test(false);
    }
}

class MyThread extends Thread {
    private MultiSemaphore sem;
    private boolean excl;

    public MyThread(MultiSemaphore sem, boolean excl) {
        this.sem = sem;
        this.excl = excl;
    }

    public void run() {
        sem.get(excl ? MultiSemaphore.EXCLUSIVE : MultiSemaphore.SHARED);
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            // ignore
        }
        sem.release();
    }
}
Avatar billede bolmer Nybegynder
13. september 2006 - 16:49 #8
Jeg var mere på udkig efter en henvisning (eller overordnet pseudokode) til noget teori omkring løsningen på dette problem end en færdig løsning. Nu har jeg fx læst i:

http://java.sun.com/docs/books/tutorial/essential/concurrency/locksync.html

at man også kan lave et objekt som man dernæst kan synkronisere på hvilket giver en mulighed for at opdatere et felt men en anden tråd læser et andet felt. På denne måde undgår man unødvendig blokering  som netop er det jeg er på udkig efter.
Avatar billede arne_v Ekspert
13. september 2006 - 17:33 #9
ArrayList er ikke thread safe (Vector er)

d.v.s. at hvis du skal bruge den i multithreaded sammenhæng skal du synkronisere
adgangen

det er ikke nok at synkronisere adgangen til det der er i ArrayList'en
Avatar billede arne_v Ekspert
13. september 2006 - 17:34 #10
selvfølgelig kan du synkronisere på forskellige ting, men den eneste grund til de
objekter i eksemplet er at man ikke kan synkronisere på long fordi de ikke er
et objekt
Avatar billede arne_v Ekspert
13. september 2006 - 17:35 #11
vigtig detalje iøvrigt: long er ikke threadsafe i Java
Avatar billede bolmer Nybegynder
13. september 2006 - 18:46 #12
Ved godt at ArrayList ikke er synkroniseret. Men jeg har lavet en klasse som arver fra ArrayList og de metoder som jeg bruger skriver jeg selv og synkronisere manuelt.

For at løse mit oprindelige problem overvejer jeg at at fjerne synkronized fra mine metoder og istedet oprette en tråd som undersøger om indkomne tråde må arbejde på listen. Den giver altså tilladelses til at tråde som ikke overlapper hinanden kan arbejde på listen, de tråde som overlapper bliver sat til at vente i en kø. På denne måde skulle det gerne være muligt at flere tråde kan arbejde samtidig i listen.
Avatar billede arne_v Ekspert
14. september 2006 - 02:28 #13
jeg har meget svært ved at tro at det er hurtigere at undersøge om om der skal låses
end det bare er at låse, gøre det og så videre
Avatar billede bolmer Nybegynder
14. september 2006 - 09:50 #14
Det er ikke så meget det som er pointen. Ideen er at jeg vil undgå at der kun er én tråd som kan arbejde på listen af gangen.

Jeg har nu løst dette ved at lave 2 synkroniserede metoder "lock" og "unlock". En tråd som vil arbejde på listen kalder altid først "lock", som undersøger om der allerede er en anden tråd der arbejder på netop det samme element i listen (en oversigt over dette opbevares i et array som kun tilgås fra "lock" og "unlock").

Er det ikke nogen konflikt kalder tråden dernæst den aktuelle metode (add, get, delete).

Disse metoder er IKKE synkroniseret men indeholder kun et atomisk kald: list.add(Object), list.get(int), list.remove(object) så der skulle ikke kunne opstå nogen overskrivninger.

På denne måde kan jeg nu fx have en tråd som læser element 40 mens en anden tråd læser element 55 mens en 3. tråd undersøger om den må skrive til fx element 66.
Avatar billede arne_v Ekspert
16. september 2006 - 05:32 #15
nu bliver jeg lidt i tvivl om hvad du vil

hvis du kun vil understøtte READ og UPDATE, så behøver du ikke locke på ArrayList'en
men kan nøjes med at locke på de enkelte elementer d.v.s. at Java synchronized kan out of the box
tillade mere end en tråd at arbejde

hvis du også vil understøtte INSERT og DELETE så er dit locking schema ikke safe og
du er nødt til at locke hele ArrayList'en
Avatar billede bolmer Nybegynder
16. september 2006 - 10:57 #16
Hvad mener du med at java sync. out of the box kan tillade mere end en tråd at arbejde?

Hvis jeg har en synchroniseret add metode så er det kun én tråd der kan bruge denne af gangen.

Andre tråde kan selvfølgelig godt arbejde på ikke synkroniseret metoder.


Antag at man har 10 tråde der gerne vil indsætte i et HashMap. De har hver en unik nøgle. Synkronisere man put metoden så kan kun en tråd arbjede på HashMappet af gangen.

Alternativt kunne man vel oprette et separat objekt Q der i konstruktøren tager HashMappet som argument og den nøgle og værdi der skal indsættes.

Trådene opretter nu først Q og kalder dernæst put indefra objektet. På denne måde har hver tråd deres egen put metode og alle tråde kan indsætte på samme tid.
Avatar billede bolmer Nybegynder
16. september 2006 - 13:02 #17
..hmm den sidste ide var ret uholdbar da man ikke kan forudsige hvor trådene befinder sig i deres tilsvarende objekter, ligeså vel som man ikke kan forudsige hvor de befinder sig i en enkelt metode.
Avatar billede arne_v Ekspert
16. september 2006 - 17:48 #18
hvis du kun læser eller opdaterer elementer kan du vel bruge normal synchronized
ikke på ArrayList'en men på elementerne

en add metode antyder at du indsætter nye elementer og så er dit eget locking schema
jo ikke godt nok og du er nød til at synkronisere adgang til ArrayList
Avatar billede arne_v Ekspert
16. september 2006 - 17:49 #19
den med HashMap forstod jeg slet ikke
Avatar billede bolmer Nybegynder
16. september 2006 - 19:20 #20
Hvordan nøjes man med at synkronisere et element i en ArrayList?

Hvis jeg fx gerne vil læse element 4 og 5 samtidig og en anden tråd kalder add som er synchronized for at indsætte et element på index 10.
Avatar billede arne_v Ekspert
16. september 2006 - 23:38 #21
lad os antage at du gemmer Person objecter i din ArrayList

hvis du ikke ændrer selve ArrayListen d.v.s. ikke fjerner eller tilføjer nye
elementer, så kan du synkronisere på elementerne:

synchronized(al.get(2)) {
    al.get(2).setFirstName("Foo");
    al.get(2).setLastName("Bar");
}

hvis alle synkroniserer på elementet, så er det thread safe og flere tråde
kan arbejde på arraylisten og det er langt hurtigere end et custom locking scheme
Avatar billede arne_v Ekspert
16. september 2006 - 23:41 #22
hvis du indsætter eller sletter så skal du låse globalt for hele arraylisten, fordi
dit custom locking scheme er ikke thread safe

eksempel

arraylisten indeholder

Jens Jensen
Hans Hansen
Niels Nielsen

tråd 1 laver

mylock(1);
firstname = al.get(1).getFirstName();
// her udføres tråd 2
lastname = al.get(1).getLastName();
myunlock(1);

tråd 2 lavet:

mylock(0);
al.remove(0);
myunlock(0);

resultatet er at tråd 1 ender op med Hans Nielsen som ikke eksisterer
Avatar billede arne_v Ekspert
16. september 2006 - 23:42 #23
i det konkrete tilfælde kunne man så æmndre koden så den kun henter element index 1
en enkelt gang

men der er andre tilfælde hvor det ikke er muligt
Avatar billede arne_v Ekspert
10. december 2006 - 00:41 #24
tid at få afsluttet her ?
Avatar billede bolmer Nybegynder
10. december 2006 - 00:43 #25
Jep du skal ha point
Avatar billede arne_v Ekspert
10. december 2006 - 00:53 #26
så må jeg jo helleere få lagt et svar
Avatar billede Ny bruger Nybegynder

Din løsning...

Tilladte BB-code-tags: [b]fed[/b] [i]kursiv[/i] [u]understreget[/u] Web- og emailadresser omdannes automatisk til links. Der sættes "nofollow" på alle links.

Loading billede Opret Preview
Kategori
Kurser inden for grundlæggende programmering

Log ind eller opret profil

Hov!

For at kunne deltage på Computerworld Eksperten skal du være logget ind.

Det er heldigvis nemt at oprette en bruger: Det tager to minutter og du kan vælge at bruge enten e-mail, Facebook eller Google som login.

Du kan også logge ind via nedenstående tjenester