Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]


Groups > de.comp.lang.java > #13131

Re: Thread-safe singleton

From Marcel Mueller <news.5.maazl@spamgourmet.org>
Newsgroups de.comp.lang.java
Subject Re: Thread-safe singleton
Date 2017-09-06 23:26 +0200
Organization MB-NET.NET for Open-News-Network e.V.
Message-ID <oopp69$mq$1@gwaiyur.mb-net.net> (permalink)
References <f1am3bF8aciU1@mid.individual.net>

Show all headers | View raw


On 06.09.17 18.29, Christian H. Kuhn wrote:
> Ich dachte, ich hätte das mit der Thread-Sicherheit inzwischen
> verstanden.

Oh, das ist ein weites Areal. Es würde mich wundern, wenn es überhaupt 
jemanden gibt, der das bis zum Ende verstanden hat.

> PMD widerspricht mir.

Wer auch immer das ist.


> final class QFdsbDatabase {
>
>      private static volatile Connection connection;

Eine statische Connection, hmm ...

>      static Connection getConnection() throws QFdsbException {
>
>          if (null == connection) {
>              synchronized (connection) {

Das wird nix. Das hatten wir ja schon.
Es darf natürlich nur *ein* Objekt geben, auf das synchronisiert wird. 
Und null ist ganz schlecht.

>                  if (null == connection) {
>
> 		[... Daten aus ini4j-File, dataSource erstellen ... ]
>
>                      try (Connection myConnection =
> dataSource.getConnection()) {

Das wird auch nichts, es sei denn es sollen geschlossene Verbindungen 
geliefert werden. Die lokale Variable im try muss weg.

>                          myConnection.setAutoCommit(true);
>                          connection = myConnection;
>                      } catch (SQLException e) {
>                          throw new QFdsbException("Problems while opening
> or closing JDBC Connection to player database",
>                                  e);
>                      }

Die Exception wird hier immer nur an einen Aufrufer ausgeliefert. Die 
anderen öffnen eine neue Verbindung - serialisiert, wenn man das Double 
Check richtig implementiert. Das kann bei systematischen Fehlern 
ziemlich langsam werden bzw. in eine DOS-Attacke ausarten.

Besser die SQLException speichern und immer wieder ausliefern.
Dabei muss man aber darauf achten, dass die null Bedingung jetzt 
entweder auf die Exception oder auf die Connection greifen muss. 
Alternativ nimmt man Typ Object und packt mal das eine und mal das 
andere rein. Lässt es sich nicht auf Connection casten, muss man halt 
throw machen.

> Lazy initialization der statischen Variablen, weil ich die geworfenen
> Exceptions sehen will, sonst hätte ich einen static block genommen.

Das ginge mit eben genannter Semantik (Exception speichern) trotzdem.


> Mit
> einem intitialization-on-demand holder vertage ich das Problem in eine
> Unterklasse.

?

> Double-checked locking sollte funktionieren, PMD behauptet
> das Gegenteil.

Implemetierungsfehler.


Es gibt aber noch eine Untiefe:
die gelieferte Connection ist mit an Sicherheit grenzender 
Wahrscheinlichkeit nicht thread-safe. Damit ist die Funktion 
getConnection in unsynchronisiertem Kontext praktisch unbrauchbar. Und 
selbst synchronisiert müsste nicht nur getConnection synchronisiert 
werden, sondern auch alle darüber laufenden Zugriffe.
Wenn aber alles rund um die Benutzung der Connection ohnehin 
synchronisiert sein muss, dann braucht man das ganze 
Double-Check-Gefuddel auch nicht mehr es wird dann ohnehin serialisiert 
gearbeitet.

Wenn hingegen umgekehrt mehrere Threads parallel die DB nutzen sollen, 
braucht man mehrere Connections und /kein/ Singleton. Das bedeutet jeder 
Thread muss die mit getConnection gelieferte Verbindung mit try/finally 
anfordern und freigeben.

Wenn jetzt wiederum nicht gewünscht ist, dass dabei jedes mal eine 
/neue/ Verbindung aufgemacht wird, braucht man eine Connection pro 
Thread. Das könnte man noch mit Thread Local Storage und Lazy Init lösen 
(unsynchronisiert).

Wenn darüber hinaus gewünscht ist, dass sich mehrere Threads Connections 
teilen, soweit sie nicht gleichzeitig eine Verbindung brauchen, dann 
braucht man einen Connection Pool. Dabei ändert sich die Semantik des 
von getConnection gelieferten Objektes so, dass es bei Close() nicht 
etwa geschlossen wird, sondern schlicht zurück in den Pool geht.
Der Zugriff auf den Pool muss natürlich synchronisiert werden. Das 
bedeutet, getConnection benötigt in jedem Fall ein synchronized. Damit 
ist der Double Check ebenfalls hinfällig.

Kurzum, es gibt kein denkbares Szenario, in dem Double-Check hier Sinn 
ergibt.


Marcel

Back to de.comp.lang.java | Previous | NextPrevious in thread | Next in thread | Find similar


Thread

Thread-safe singleton "Christian H. Kuhn" <qno-news@qno.de> - 2017-09-06 18:29 +0200
  Re: Thread-safe singleton Patrick Roemer <sangamon@netcologne.de> - 2017-09-06 21:06 +0200
  Re: Thread-safe singleton v_borchert@despammed.com (Volker Borchert) - 2017-09-06 20:02 +0000
    Re: Thread-safe singleton Patrick Roemer <sangamon@netcologne.de> - 2017-09-06 23:27 +0200
      Re: Thread-safe singleton v_borchert@despammed.com (Volker Borchert) - 2017-09-07 16:45 +0000
      Re: Thread-safe singleton "Christian H. Kuhn" <qno-news@qno.de> - 2017-09-11 01:08 +0200
        Re: Thread-safe singleton Patrick Roemer <sangamon@netcologne.de> - 2017-09-11 18:14 +0200
  Re: Thread-safe singleton Marcel Mueller <news.5.maazl@spamgourmet.org> - 2017-09-06 23:26 +0200
    Re: Thread-safe singleton Claus Reibenstein <4spamersonly@kabelmail.de> - 2017-09-10 13:52 +0200

csiph-web