Lokale Referenz als Alternative zu Lock/Synchronized

benurb

Mitglied
Ich habe mir folgenden Gedankengang überlegt. Nehmen wir an ich habe zum Beispiel folgende Klasse:
Java:
public class Test
{
	private Object _obj;
	
	public void methodCallNTS()
	{
		if(_obj != null)
		{
			_obj.test();
		}
	}
	
	public void methodCallTS()
	{
		Object obj = _obj;
		if(obj != null)
		{
			obj.test();
		}
	}
	
	public void setObj(Object obj)
	{
		_obj = obj;
	}
}

Nun ist die Methode methodCallNTS() ja nicht Thread-Safe da theoretisch zwischen dem null-Check und dem Methodenaufruf von test() theoretisch ein anderer Thread ein setObj(null) ausführen könnte. Die Methode methodCallTS() müsste aber meiner Logik nach Thread-Safe sein, weil ja die Referenz unverändert bleibt und der null-Check und der Methodenaufruf von test() definitiv auf dasselbe Objekt ausgeführt werden. Oder ist mein Gedankengang hier schon falsch?
Falls nicht, wäre es dann in solchen einfachen Szenarien eine bessere Alternative zu Synchronized oder Read/Write-Locks? Falls nicht, welche Vorteile ergeben sich durch die Nutzung von Synchronized oder Locks?

Danke im Voraus :)
 
Zuletzt bearbeitet:

Fabio Hellmann

Erfahrenes Mitglied
Hi,
also dein Konstrukt ist meiner Ansicht nach nicht Thread-Safe, da...
Java:
Object obj = _obj;
kein neues Objekt anlegt, sondern nur eine neue Referenz auf das Objekt - welches du in der setObj(...)-Methode übergibst - erstellt.

Dadurch kann folgendes passieren:
Code:
Thread 1 -> methodCallTS() -> neue Referenz erstellen 'Object obj = _obj'
Thread 1 -> methodCallTS() -> if-Bedingung überprüfen -> true
Thread 2 -> setObj(null) -> _obj = null
Thread 1 -> methodCallTS() -> obj.test() -> NullPointerException!

Als Syncronisierungsmaßnahme könntest du z.B. 'volatile' verwenden. Wobei auch das nicht die beste Lösung wäre.

Deswegen würde ich dir empfehlen lieber mit 'syncronized' zu arbeiten. Das ist meiner Ansicht nach die beste Möglichkeit Thread-Safe zu bleiben. Zumindest in diesem Kontext.

Gruß

Fabio
 
Zuletzt bearbeitet von einem Moderator:

sheel

I love Asm
@Fabio: Das = erstellt doch keine Referenz
auf die andere Variable, sondern auf das Objekt selbst?

Was mir eher Sorgen macht, ob das ganze innere Zeug
vom = (Referenzzähler erhöhen etc.) als Atomic durchgeht.


Aber ja, synchronized etc. ist sicher.
 

benurb

Mitglied
Danke für eure Antworten :)
Das ist eine gute Frage, sheel. Vielleicht weiß das zufällig jemand?
Also die Idee hinter dem methodCallTS()-Code war:
Es wird zunächst die Referenz aus _obj in obj kopiert. Falls nun zwischen null-Check und Methodenaufruf die Referenz von _obj verändert wird, sollte ja trotzdem noch die alte Referenz in obj erhalten bleiben (und auch nicht der GC zum Opfer fallen, weil sie ja noch referenziert wird). Also das war meine Idee. Wie gesagt ich hab keine Ahnung wie das in Java läuft. Wird wirklich die komplette Referenz von _obj in obj kopiert oder wird eine Referenz auf die Referenz erstellt?

Edit:
Ok ich revidiere die letzte Frage. Die hast du ja schon beantwortet, sheel. Habe ich jetzt grade erst richtig kapiert. Aber die erste Frage bleibt. Weiß jemand zufällig, ob = atomar ist?
 
Zuletzt bearbeitet:

benurb

Mitglied
Alles klar. Danke euch :)

Das heißt als Fazit bleibt:
methodCallTS() ist nicht Thread-Safe, weil die Zuweisung in Java nicht atomar ist. Um eine Thread-Sicherheit in der gegebenen Klasse zu erreichen sollten stattdessen die Methoden methodCallNTS() und setObj() synchronized gemacht werden. Richtig?
 

sheel

I love Asm
Genau.
Java:
public class Test
{
    private Object _obj;
    
    synchronized public void methodCallTS()
    {
        if(_obj != null)
        {
            _obj.test();
        }
    }

    synchronized public void setObj(Object obj)
    {
        _obj = obj;
    }
}
Genauer gesagt kann von derjenigen Test-Instanz
nur eine sync-Methode gleichzeitig laufen.

Bei komplexen Sachen kann man mit Locks (RW-Locks etc.)
eventuell mehr Geschwindigeit rausholen, weil man gezielt nur das sperrt, was nötig ist...
aber das macht die Programmiererei auch komplizierter.