[java] Inheritance & synchronization

Pagina: 1
Acties:

  • B-Man
  • Registratie: Februari 2000
  • Niet online
Ik heb een klasse (Y) die een hoofdklasse (X) "extend". Als ik nu in klasse Y een aantal synchronized methoden heb, en een non-synchronized methode met nested synchronization block, hoe lock ik dan hetzelfde object?

In pseudo-code:

Java:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class X {}
class Y extends X {
    public synchronized void test() {
        ...
        notifyAll();
    }

    public void lock() {
        while (!conditie)) {
            synchronized(this) {
                try {
                   wait();
                } catch(InterruptedException e) {}
            }
        }
    }
}


Een aantal threads "wacht" in Y.lock(); Na een bepaald event wordt Y.test() aangeroepen. Nu blijven de threads in Y.lock() echter wachten.

Ik heb met google gezocht, en er zojuist "Concurrent programming in Java" van Doug Lea op nageslagen, maar kan nergens duidelijk vinden hoe ik dit oplos.

Als ik het synchronized blok uit lock() weghaal, en de hele methode synchronized maak, is het probleem opgelost. Dit wil ik echter niet vanwege het feit dat deze methode zeer vaak wordt aangeroepen, en er eigenlijk alleen gelocked moet worden als er niet aan een conditie voldaan wordt, die vrijwel nooit voorkomt.

Kortom: als synchronisatie in een methode op een ander object synchroniseerd als een volledig gesynchroniseerde methode, hoe zorg ik er dan voor dat ze elkaar wel "zien"?

  • Soultaker
  • Registratie: September 2000
  • Laatst online: 01:56
Vergis ik me nu, of heeft dit probleem niets met inheritance te maken?

Verder zou ik zeggen dat jouw methode gewoon goed zou moeten zijn. Misschien is het een implementatiefout; welke JVM gebruik je, op welke platform? Het zou ook een optimalisatiefout kunnen zijn.

Als hack zou ik kunnen suggereren om een aparte synchronized method te maken, die je aanroept in plaats van je synchronized-sectie te gebruiken, want volgens jou gaat dat wel goed.

edit:
Weet je zeker dat je huidige methode veilig is? Als ik het goed begrijp, moet lock() wachten tot een bepaalde conditie geldt en wordt die conditie (misschien) opgeheven na een aanroep van test(). Als dit de situatie is, dan kan het dus voorkomen dat de lock() functie uitgevoerd wordt, de conditie niet opgaat, maar voordat je in je synchronized sectie komt, test() de conditie alsnog waarmaakt. Dat lijkt me niet de bedoeling (dan krijg je een deadlock).

Dat is verder je probleem hier niet, maar misschien is het wel een probleem. Weet je trouwens zeker dat je op hetzelfde object synchroniseerd? Debug-print 'this' anders eens in lock() en test()?

[ Voor 43% gewijzigd door Soultaker op 30-07-2003 17:26 ]


  • B-Man
  • Registratie: Februari 2000
  • Niet online
Soultaker: Linux, Sun JRE 1.4.2-b28 in server mode;

Ik heb inderdaad al een extra methode aangemaakt, alleen kan ik er niet goed tegen als er van dit soort "onverklaarbare" zaken gebeuren ;)

--Edit--
Ja, mijn huidige methode is veilig genoeg. Zoals ik al aangaf zal de conditie in 99,999% van de gevallen gelden.
test() voert wat zaken uit, en enkel als die _slagen_ wordt notifyAll() aangeroepen. test() wordt alleen uitgevoerd als niet aan de conditie voldaan wordt. Tevens is het al zo dat -mocht de situatie die je beschrijft voorkomen- de methoden die lock() aanroepen de situatie af kunnen handelen. Dit scheelt ontzettend veel synchronization overhead.

Maar goed, hiervan ben ik me dus bewust; dat is dan ook niet waar mijn vraag over gaat.

Ik ga er vanuit dat een synchronized methode en een synchronized block in dezelfde klasse (geen inherited methoden trouwens) op hetzelfde object synchroniseren.

[ Voor 67% gewijzigd door B-Man op 30-07-2003 17:36 ]


  • Soultaker
  • Registratie: September 2000
  • Laatst online: 01:56
B-Man schreef op 30 July 2003 @ 17:28:
Ik ga er vanuit dat een synchronized methode en een synchronized block in dezelfde klasse (geen inherited methoden trouwens) op hetzelfde object synchroniseren.
Elk object fungeert als monitor. Je moet de twee methoden dan ook wel op hetzelfde object (instantie van een klasse) aanroepen. Daarom dacht ik: probeer de boel eens te debug printen; als je op een andere instantie wait dan je notified, dan werkt het natuurlijk niet.

Sowieso is enige vorm van debuginformatie wel prettig, anders kan ik niet veel meer zeggen, dan dat het een implementatiefout zou kunnen zijn.

  • B-Man
  • Registratie: Februari 2000
  • Niet online
Soultaker: ik werk met een synchronized Singleton pattern: er dus slechts een instantie van het object. Dit heb ik al gecontroleerd (dat was het eerste).

Ik programmeer al langer multi-threaded, en ken de meeste zaken wel. Dit heb ik echter nog niet eerder meegemaakt.

Als ik met een klasse werk die geen andere (eigen) klasse extend, werkt het wel.
Het lijkt er dus op dat een synchronized method op een andere instantie/object synchroniseerd als synchronized(this). Ik kan er echter niet achter komen op welke object de methode synchroniseerd.

--kleine edit--

Oh, en debug printen doe ik tijdens development altijd, dat scheelt een hoop tijd en energie. Maar goed, ik lock dus op _hetzelfde_ object (althans, concreet: dezelfde instantie van een object). De call naar wait() in synchronized(this) heeft echter een andere monitor dan de synchronized method in dezelfde klasse.

[ Voor 24% gewijzigd door B-Man op 30-07-2003 23:55 ]


  • Soultaker
  • Registratie: September 2000
  • Laatst online: 01:56
B-Man schreef op 30 juli 2003 @ 23:53:
Als ik met een klasse werk die geen andere (eigen) klasse extend, werkt het wel.
Het lijkt er dus op dat een synchronized method op een andere instantie/object synchroniseerd als synchronized(this). Ik kan er echter niet achter komen op welke object de methode synchroniseerd.
Nee, dat is volgens de specificatie niet zo. Elk object stelt precies één monitor voor. Een synchronized method zou equivalent moeten zijn met een method declaratie die begint met een "synchronized(this) {}".
Oh, en debug printen doe ik tijdens development altijd, dat scheelt een hoop tijd en energie. Maar goed, ik lock dus op _hetzelfde_ object (althans, concreet: dezelfde instantie van een object). De call naar wait() in synchronized(this) heeft echter een andere monitor dan de synchronized method in dezelfde klasse.
Waar leidt je die uit af? Dat zou namelijk niet moeten. Heb je misschien een klein stukje volledige code, die ik hier kan uitvoeren om het probleem te verifiëren?

  • B-Man
  • Registratie: Februari 2000
  • Niet online
Soultaker: zal na het weekend wat code posten, ok?
Hoe ik het weet? "this" levert hetzelfde object op, en toch blijft een call naar wait() "hangen" als erna door een andere thread notifyAll() wordt aangeroepen. Pas als ik de methode die wait() aanroept synchronized maak, resulteert de notifyAll() call in een wakeup van de thread die wait() aanriep.

M.b.t. de specificatie heb je gelijk, daarom is het juist zo vreemd dat het niet werkt in mijn app.
Pagina: 1