Frage

Jeder Vorsichtsmaßnahmen in Bezug auf Java Date nicht Thread-sicher zu sein, und ich verstehe das Konzept theoretisch.

Aber ich bin nicht in der Lage zu visualisieren, was tatsächlichen Probleme, die wir aufgrund dieser stellen kann. Sprich: Ich habe ein Date Feld in einer Klasse und das gleiche wird in verschiedenen Methoden in der Klasse (Formatierung Daten) in einer Multi-Threaded-Umgebung verwendet.

Wird diese Ursache:

  • jede Ausnahme wie Format Ausnahme
  • Diskrepanz in Daten
  • andere Frage?

Sie bitte auch erklären, warum.

War es hilfreich?

Lösung

Lassen Sie uns versuchen Sie es aus.

Hier ist ein Programm, in dem mehrere Threads eine gemeinsame SimpleDateFormat verwenden.

Programm :

public static void main(String[] args) throws Exception {

    final DateFormat format = new SimpleDateFormat("yyyyMMdd");

    Callable<Date> task = new Callable<Date>(){
        public Date call() throws Exception {
            return format.parse("20101022");
        }
    };

    //pool with 5 threads
    ExecutorService exec = Executors.newFixedThreadPool(5);
    List<Future<Date>> results = new ArrayList<Future<Date>>();

    //perform 10 date conversions
    for(int i = 0 ; i < 10 ; i++){
        results.add(exec.submit(task));
    }
    exec.shutdown();

    //look at the results
    for(Future<Date> result : results){
        System.out.println(result.get());
    }
}

Führen Sie diese ein paar Mal und Sie werden sehen:

Ausnahmen :

Hier sind einige Beispiele:

1.

Caused by: java.lang.NumberFormatException: For input string: ""
    at java.lang.NumberFormatException.forInputString(NumberFormatException.java:48)
    at java.lang.Long.parseLong(Long.java:431)
    at java.lang.Long.parseLong(Long.java:468)
    at java.text.DigitList.getLong(DigitList.java:177)
    at java.text.DecimalFormat.parse(DecimalFormat.java:1298)
    at java.text.SimpleDateFormat.subParse(SimpleDateFormat.java:1589)

2.

Caused by: java.lang.NumberFormatException: For input string: ".10201E.102014E4"
    at sun.misc.FloatingDecimal.readJavaFormatString(FloatingDecimal.java:1224)
    at java.lang.Double.parseDouble(Double.java:510)
    at java.text.DigitList.getDouble(DigitList.java:151)
    at java.text.DecimalFormat.parse(DecimalFormat.java:1303)
    at java.text.SimpleDateFormat.subParse(SimpleDateFormat.java:1589)

3.

Caused by: java.lang.NumberFormatException: multiple points
    at sun.misc.FloatingDecimal.readJavaFormatString(FloatingDecimal.java:1084)
    at java.lang.Double.parseDouble(Double.java:510)
    at java.text.DigitList.getDouble(DigitList.java:151)
    at java.text.DecimalFormat.parse(DecimalFormat.java:1303)
    at java.text.SimpleDateFormat.subParse(SimpleDateFormat.java:1936)
    at java.text.SimpleDateFormat.parse(SimpleDateFormat.java:1312)

falsche Ergebnisse :

Sat Oct 22 00:00:00 BST 2011
Thu Jan 22 00:00:00 GMT 1970
Fri Oct 22 00:00:00 BST 2010
Fri Oct 22 00:00:00 BST 2010
Fri Oct 22 00:00:00 BST 2010
Thu Oct 22 00:00:00 GMT 1970
Fri Oct 22 00:00:00 BST 2010
Fri Oct 22 00:00:00 BST 2010
Fri Oct 22 00:00:00 BST 2010
Fri Oct 22 00:00:00 BST 2010

Die korrekten Ergebnisse :

Fri Oct 22 00:00:00 BST 2010
Fri Oct 22 00:00:00 BST 2010
Fri Oct 22 00:00:00 BST 2010
Fri Oct 22 00:00:00 BST 2010
Fri Oct 22 00:00:00 BST 2010
Fri Oct 22 00:00:00 BST 2010
Fri Oct 22 00:00:00 BST 2010
Fri Oct 22 00:00:00 BST 2010
Fri Oct 22 00:00:00 BST 2010
Fri Oct 22 00:00:00 BST 2010

Ein weiterer Ansatz, um sicher DateFormats in einer Multi-Threaded-Umgebung zu verwenden ist, eine ThreadLocal Variable zu verwenden, um das DateFormat Objekt zu halten, was bedeutet, dass jeder Thread seine eigene Kopie hat und muss nicht für andere Threads warten, um sich zu lösen . Dies ist, wie:

public class DateFormatTest {

  private static final ThreadLocal<DateFormat> df = new ThreadLocal<DateFormat>(){
    @Override
    protected DateFormat initialValue() {
        return new SimpleDateFormat("yyyyMMdd");
    }
  };

  public Date convert(String source) throws ParseException{
    Date d = df.get().parse(source);
    return d;
  }
}

Hier ist eine gute Post mit mehr Details.

Andere Tipps

würde ich Daten Korruption erwarten - zum Beispiel wenn Sie zwei Termine zur gleichen Zeit sind Parsen, können Sie einen Anruf von Daten von einem anderen verunreinigt haben.

Es ist leicht, sich vorzustellen, wie das passieren konnte: oft Parsen beinhaltet eine bestimmte Menge an Zustand beibehalten wird, was Sie bisher gelesen habe. Wenn zwei Threads auf dem gleichen Zustand sowohl Trampeln, werden Sie Probleme bekommen. Zum Beispiel setzt DateFormat ein calendar Feld vom Typ Calendar, und auf den Code von SimpleDateFormat suchen, einige Methoden aufrufen calendar.set(...) und andere Anruf calendar.get(...). Dies ist eindeutig nicht Thread-sicher.

Ich habe nicht sah in der genau Details, warum DateFormat ist nicht Thread-sicher, aber für mich ist es genug, um zu wissen, dass es is unsicher ohne Synchronisation - die genaue Art und Weise von nicht-Sicherheit könnte auch zwischen den Versionen ändern.

Persönlich würde ich die Parser verwenden, um von Joda Zeit statt, wie sie ist fädelt sicher - und Joda Zeit ist ein viel besseres Datum und Zeit API beginnen mit:)

Wenn Sie mit Java 8, dann können Sie a href verwenden <= "https://docs.oracle.com/javase/8/docs/api/java/time/format/DateTimeFormatter.html" rel = "noreferrer „> DateTimeFormatter .

  

Ein Formatierer aus einem Muster erstellt wird, kann beliebig oft verwendet werden, wie   notwendig, es ist unveränderlich und ist Thread-sicher.

Code:

LocalDate date = LocalDate.now();
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
String text = date.format(formatter);
System.out.println(text);

Ausgabe:

2017-04-17

Grob gesagt, dass Sie sollten nicht ein DateFormat als Instanzvariable eines von vielen Themen, oder static zugegriffen Objekts definieren.

  

Datumsformate werden nicht synchronisiert. Es wird empfohlen, eigene Format Instanzen für jeden Thread zu erstellen.

Also, falls Ihr Foo.handleBar(..) von mehreren Threads zugegriffen wird, statt:

public class Foo {
    private DateFormat df = new SimpleDateFormat("dd/mm/yyyy");

    public void handleBar(Bar bar) {
        bar.setFormattedDate(df.format(bar.getStringDate());  
    }
}

Sie verwenden sollten:

public class Foo {

    public void handleBar(Bar bar) {
        DateFormat df = new SimpleDateFormat("dd/mm/yyyy");
        bar.setFormattedDate(df.format(bar.getStringDate());  
    }
}

Auch in allen Fällen haben keine static DateFormat

Wie von Jon Skeet erwähnt, können Sie sowohl statische haben und eine gemeinsame Instanz-Variablen, falls Sie externe Synchronisation durchführen (das heißt synchronized den Einsatz rund um Anrufe an die DateFormat)

  

Datumsformate werden nicht synchronisiert. Es wird empfohlen, für jeden Thread getrennt Format Instanzen zu erstellen. Wenn mehrere Threads gleichzeitig ein Format zuzugreifen, müssen sie synchronisiert werden   nach außen.

Dieses Mittel nehme an, Sie ein Objekt von Dateformat haben und Sie Zugriff auf denselben Gegenstand von zwei verschiedenen Threads und Sie rufen Format Methode auf das Objekt sowohl Gewinde auf dem gleichen Verfahren zur gleichen Zeit auf dem gleichen Objekt betreten werden, so dass Sie visualisieren es in der richtigen Ergebnis nicht führen

Wenn Sie mit Dateformat zu arbeiten haben jeder, wie dann sollten Sie etwas tun

public synchronized myFormat(){
// call here actual format method
}

sind Daten beschädigt. Gestern bemerkte ich es in meinem Multi-Thread-Programm, wo ich statisches DateFormat Objekt hatte und nannte seine format() für Werte über JDBC lesen. Ich hatte SQL-Anweisung SELECT, wo ich das gleiche Datum mit unterschiedlichen Namen lesen (SELECT date_from, date_from AS date_from1 ...). Solche Aussagen wurden in 5 Verknüpfungen für verschiedene Termine in WHERE clasue verwenden. Termine sahen „normal“, aber sie unterschieden sich in Wert - während alle Termine aus dem gleichen Jahr waren nur Monat und Tag gewechselt

.

Andere Antworten zeigen Ihnen den Weg, um solche Korruption zu vermeiden. Ich habe meinen DateFormat nicht statisch, jetzt ist es ein Mitglied einer Klasse ist, die SQL-Anweisungen aufruft. Getestet habe ich auch statische Version mit synchronisiert werden. Beide arbeiteten gut mit keinem Unterschied in der Leistung.

Die Spezifikationen von Format, Number, Dateformat, Message usw. wurden nicht als Thread-sicher gestaltet. die Parse-Methode fordert Calendar.clone() Methode auch, und es wirkt Kalender Fußabdrücke so viele Threads gleichzeitig Parsen wird das Klonieren der Kalender-Instanz ändern.

Für weitere, diese Fehler sind Berichte wie dieser und < a href = "http://bugs.sun.com/bugdatabase/view_bug.do;jsessionid=e7555c4e5577a514744f197010c9?bug_id=4264153" rel = "nofollow"> diese , mit den Ergebnissen der Date Thread-Sicherheit Ausgabe.

In der besten Antwort dogbane gab ein Beispiel von parse Funktion und was es führt. Im Folgenden ist ein Code, lassen Sie sich format Funktion überprüfen.

, dass Hinweis, wenn Sie die Anzahl der Testamentsvollstrecker (gleichzeitige Threads) ändern Sie unterschiedliche Ergebnisse erhalten. Aus meinen Experimenten:

  • Leave newFixedThreadPool auf 5 und die Schleife wird jedes Mal scheitern.
  • Wird auf 1 gesetzt und die Schleife wird immer Arbeit (natürlich wie alle Aufgaben, die man tatsächlich ausgeführt werden durch ein)
  • auf 2 und die Schleife hat nur etwa 6% Chance zu arbeiten.

Ich vermute, YMMV je nach Prozessor.

Die format Funktion nicht durch die Zeit von einem anderen Thread zu formatieren. Dies liegt daran, intern format Funktion calendar Objekt verwendet, die bei der Inbetriebnahme der format Funktion eingestellt ist. Und das calendar Objekt ist eine Eigenschaft der SimpleDateFormat Klasse. Seufz ...

/**
 * Test SimpleDateFormat.format (non) thread-safety.
 *
 * @throws Exception
 */
private static void testFormatterSafety() throws Exception {
    final SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    final Calendar calendar1 = new GregorianCalendar(2013,1,28,13,24,56);
    final Calendar calendar2 = new GregorianCalendar(2014,1,28,13,24,56);
    String expected[] = {"2013-02-28 13:24:56", "2014-02-28 13:24:56"};

    Callable<String> task1 = new Callable<String>() {
        @Override
        public String call() throws Exception {
            return "0#" + format.format(calendar1.getTime());
        }
    };
    Callable<String> task2 = new Callable<String>() {
        @Override
        public String call() throws Exception {
            return "1#" + format.format(calendar2.getTime());
        }
    };

    //pool with X threads
    // note that using more then CPU-threads will not give you a performance boost
    ExecutorService exec = Executors.newFixedThreadPool(5);
    List<Future<String>> results = new ArrayList<>();

    //perform some date conversions
    for (int i = 0; i < 1000; i++) {
        results.add(exec.submit(task1));
        results.add(exec.submit(task2));
    }
    exec.shutdown();

    //look at the results
    for (Future<String> result : results) {
        String answer = result.get();
        String[] split = answer.split("#");
        Integer calendarNo = Integer.parseInt(split[0]);
        String formatted = split[1];
        if (!expected[calendarNo].equals(formatted)) {
            System.out.println("formatted: " + formatted);
            System.out.println("expected: " + expected[calendarNo]);
            System.out.println("answer: " + answer);
            throw new Exception("formatted != expected");
        /**
        } else {
            System.out.println("OK answer: " + answer);
        /**/
        }
    }
    System.out.println("OK: Loop finished");
}

Wenn mehrere Threads zu manipulieren / eine einzelne Instanz und Date Synchronisation nicht genutzt, den Zugriff, dann ist es möglich, verschlüsselten Ergebnisse zu erhalten. Das ist, weil mehrere nicht-atomaren Operationen Zustand zu ändern könnten oder Speicher zu sehen inkonsequent.

Das ist mein einfacher Code, zeigt Dateformat nicht sicher ist, fädelt.

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;

public class DateTimeChecker {
    static DateFormat df = new SimpleDateFormat("EEE MMM dd kk:mm:ss z yyyy", Locale.ENGLISH);
    public static void main(String args[]){
       String target1 = "Thu Sep 28 20:29:30 JST 2000";
       String target2 = "Thu Sep 28 20:29:30 JST 2001";
       String target3 = "Thu Sep 28 20:29:30 JST 2002";
       runThread(target1);
       runThread(target2);
       runThread(target3);
   }
   public static void runThread(String target){
       Runnable myRunnable = new Runnable(){
          public void run(){

            Date result = null;
            try {
                result = df.parse(target);
            } catch (ParseException e) {
                e.printStackTrace();
                System.out.println("Ecxfrt");
            }  
            System.out.println(Thread.currentThread().getName() + "  " + result);
         }
       };
       Thread thread = new Thread(myRunnable);

       thread.start();
     }
}

Da alle Fäden die gleiche Simple Objekt verwenden, es wirft die folgende Ausnahme.

Exception in thread "Thread-0" Exception in thread "Thread-2" Exception in thread "Thread-1" java.lang.NumberFormatException: multiple points
at sun.misc.FloatingDecimal.readJavaFormatString(Unknown Source)
at sun.misc.FloatingDecimal.parseDouble(Unknown Source)
at java.lang.Double.parseDouble(Unknown Source)
at java.text.DigitList.getDouble(Unknown Source)
at java.text.DecimalFormat.parse(Unknown Source)
at java.text.SimpleDateFormat.subParse(Unknown Source)
at java.text.SimpleDateFormat.parse(Unknown Source)
at java.text.DateFormat.parse(Unknown Source)
at DateTimeChecker$1.run(DateTimeChecker.java:24)
at java.lang.Thread.run(Unknown Source)
java.lang.NumberFormatException: multiple points
at sun.misc.FloatingDecimal.readJavaFormatString(Unknown Source)
at sun.misc.FloatingDecimal.parseDouble(Unknown Source)
at java.lang.Double.parseDouble(Unknown Source)
at java.text.DigitList.getDouble(Unknown Source)
at java.text.DecimalFormat.parse(Unknown Source)
at java.text.SimpleDateFormat.subParse(Unknown Source)
at java.text.SimpleDateFormat.parse(Unknown Source)
at java.text.DateFormat.parse(Unknown Source)
at DateTimeChecker$1.run(DateTimeChecker.java:24)
at java.lang.Thread.run(Unknown Source)
java.lang.NumberFormatException: multiple points
at sun.misc.FloatingDecimal.readJavaFormatString(Unknown Source)
at sun.misc.FloatingDecimal.parseDouble(Unknown Source)
at java.lang.Double.parseDouble(Unknown Source)
at java.text.DigitList.getDouble(Unknown Source)
at java.text.DecimalFormat.parse(Unknown Source)
at java.text.SimpleDateFormat.subParse(Unknown Source)
at java.text.SimpleDateFormat.parse(Unknown Source)
at java.text.DateFormat.parse(Unknown Source)
at DateTimeChecker$1.run(DateTimeChecker.java:24)
at java.lang.Thread.run(Unknown Source)

Aber wenn wir verschiedene Objekte zu verschiedenen Threads passieren, der Code ausgeführt wird ohne Fehler.

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;

public class DateTimeChecker {
    static DateFormat df;
    public static void main(String args[]){
       String target1 = "Thu Sep 28 20:29:30 JST 2000";
       String target2 = "Thu Sep 28 20:29:30 JST 2001";
       String target3 = "Thu Sep 28 20:29:30 JST 2002";
       df = new SimpleDateFormat("EEE MMM dd kk:mm:ss z yyyy", Locale.ENGLISH);
       runThread(target1, df);
       df = new SimpleDateFormat("EEE MMM dd kk:mm:ss z yyyy", Locale.ENGLISH);
       runThread(target2, df);
       df = new SimpleDateFormat("EEE MMM dd kk:mm:ss z yyyy", Locale.ENGLISH);
       runThread(target3, df);
   }
   public static void runThread(String target, DateFormat df){
      Runnable myRunnable = new Runnable(){
        public void run(){

            Date result = null;
            try {
                result = df.parse(target);
            } catch (ParseException e) {
                e.printStackTrace();
                System.out.println("Ecxfrt");
            }  
            System.out.println(Thread.currentThread().getName() + "  " + result);
         }
       };
       Thread thread = new Thread(myRunnable);

       thread.start();
   }
}

Dies sind die Ergebnisse.

Thread-0  Thu Sep 28 17:29:30 IST 2000
Thread-2  Sat Sep 28 17:29:30 IST 2002
Thread-1  Fri Sep 28 17:29:30 IST 2001
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top