2016-09-07 9 views
2

Я пытаюсь понять необходимость использования ThreadLocal. Многие люди упоминают, что ThreadLocal следует использовать для потоковой передачи SimpleDateFormat, но они не упоминают, как будет выглядеть искаженный SimpleDateFormat, если ThreadLocal не используется. Я пробую следующий код, кажется, все в порядке, я не вижу искаженного SimpleDateFormat.Несколько потоков используют один и тот же SimpleDateFormat без ThreadLocal

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

public class ThreadLocalTest { 
    private static final SimpleDateFormat dateFormat = new SimpleDateFormat("dd/MM/yyyy"); 
    private static final Date TODAY = new Date(); 
    private static final String expected = "07/09/2016"; 
    public static void main(String[] args) { 
    for (int i = 0; i < 10000; i++) { 
     new Thread(new Runnable() { 
     @Override 
     public void run() { 
      for (int j = 0; j < 1000; j++) { 
      String real = dateFormat.format(TODAY); 
      if (!real.equals(expected)) { 
       throw new RuntimeException("Mangled SimpleDateFormat"); 
      } 
      } 
     } 
     }).start(); 
    } 
    } 
} 

Как я могу произвести исключение как NumberFormatException, потому что я не использую ThreadLocal?

+1

вы запускали его всего лишь 1000 раз и использовали 'println', который является синхронизированным методом. Вместо этого запустите его навсегда, ничего не печатайте и бросайте исключение, когда вы получаете строку, отличную от ожидаемой. –

+1

Кажется, что если я использую метод 'parse', я бы смог легко увидеть исключение. – user2018791

ответ

3

Решающим моментом является то, что реализация SimpleDateFormat является не нить безопасной.

Это не значит, что это вызовет исключение. Это хуже: может быть, иногда общий форматтер просто даст вам неправильный результат!

Вы знаете, если «проблемы с несколькими потоками» красиво бросают на вас исключения ... люди будут гораздо меньше их бояться. Потому что у нас был бы прямой намек на то, что что-то пошло не так.

Вместо этого все пойдет не так - и незамеченным.

Предложение: улучшить свой тест на

  1. всегда форматировать же Дата объекта
  2. проверить, что результат форматирования что дата, как и ожидалось (например, путем сравнения его против результат первоначальной, первой операции форматирования)

И, конечно же, только несоответствие печати es, так что заметьте, когда они произойдут. Или лучше: выбросьте свое собственное исключение из-за несоответствия!

EDIT: Получается, что «лучше» способ обеспечить несоответствия, чтобы не использовать форматирования но разбор!

Наконец, чтобы обратиться к другому комментарию: конечно, несоответствия могут возникать только для объектов, которые совместно используют между несколькими потоками. Когда каждый поток имеет свой собственный объект формата, чем нет общего доступа; таким образом, не проблема.

+0

Даже я обновил свой тест (см. Отредактированный в моем вопросе). Я не видел исключения. Было бы хорошо, если бы я мог увидеть исключение, потому что я не использую 'ThreadLocal'. – user2018791

+0

Я не знаю, что еще сказать. Неважно, что вы думаете «было бы хорошо». Java-язык и JVM делают то, что они; независимо от того, что мы думаем об этом. Как сказано: когда несколько потоков ** повреждают ** общий объект, тогда исключений нет. Вы просто получаете повреждение данных. В этом весь смысл, который делает ** совместное использование ** между потоками ** трудным **. И вам или мне не нравится, что это не изменит. – GhostCat

+0

Спасибо. Задайте еще один вопрос. Какая разница, если я просто просто новый «SimpleDateFormat», когда я создаю «Thread» по сравнению с использованием «ThreadLocal »? – user2018791

0

Просто запустите этот код, вы получите «java.lang.NumberFormatException». Если не произойдет, запустите еще несколько раз

import java.text.ParseException; 
import java.text.SimpleDateFormat; 

public class ThreadLocalDemo1 implements Runnable { 
private static final SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); 

public static void main(String[] args) { 
    ThreadLocalDemo1 td = new ThreadLocalDemo1(); 
    Thread t1 = new Thread(td, "Thread-1"); 
    Thread t2 = new Thread(td, "Thread-2"); 
    t1.start(); 
    t2.start(); 
} 

@Override 
public void run() { 
    for (int i = 0; i < 100; i++) { 

     System.out.println("Thread run execution started for " + Thread.currentThread().getName()); 
     System.out.println("Date formatter pattern is " + simpleDateFormat.toPattern()); 

     try { 
      System.out.println("Formatted date is " + simpleDateFormat.parse("2013-05-24 06:02:20")); 
     } catch (ParseException pe) { 
      pe.printStackTrace(); 
     } 

     System.out.println("========================================================="); 
    } 
} 

}

0

Дата форматы не поточно.

Я думаю, что если вы отформатируете тот же день, который невозможно воспроизвести, вы должны использовать две разные даты или формат, а также второй и даты, которые имеют разные секунды и т. Д. Формат даты использует календарь под капотом, на котором он устанавливает дату ,Если первый поток устанавливает дату и начинает форматирование строки, а другой поток с другой датой приходит и устанавливает его в тот же календарь, вы получите неправильный вывод.

После кода производит исключение/ошибку:

final Date today = new Date(); 
    String expectedToday = dateFormat.format(today); 

    Date yesterday = new Date(today.getTime() - TimeUnit.DAYS.toMillis(1)); 
    String expectedYesterday = dateFormat.format(yesterday); 

    for (int i = 0; i < 2; i++) { 
     new Thread(new Runnable() { 
      @Override 
      public void run() { 
       while (true) { 
        String format = dateFormat.format(today); 
        if (!expectedToday.equals(format)) { 
         System.out.println("error: " + format + " " + expectedToday);//Throw exception if you want 
        } 

        format = dateFormat.format(yesterday); 
        if (!expectedYesterday.equals(format)) { 
         System.out.println("error: " + format + " " + expectedYesterday);//Throw exception if you want 
        } 
       } 
      } 
     }).start(); 
    } 
0

Один из способов, которыми SimpleDateFormat не Потокобезопасными является то, что она имеет внутренний calendar поля, которое держит Calendar объекта. В значительной степени первое, что делает SimpleDateFormat, до фактического форматирования даты - это вызов this.calendar.setTime(theDateYouPassedIn), отсутствие синхронизации или блокировки. Я не уверен, что это единственный способ, но проверить код достаточно просто.

Таким образом, один из способов получить SimpleDateFormat для отказа - использовать даты, которые будут выдавать разные выходные данные в разных потоках. Вот пример:

public class NotThreadSafe 
{ 
    private static final SimpleDateFormat dateFormat = new SimpleDateFormat("dd/MM/yyyy"); 

    public static void main(String[] args) { 
    Date dref = new Date(); 

    // Dates for yesterday and tomorrow 
    Date[] ds = new Date[] { 
     new Date(dref.getTime() - (24L * 60L * 60L * 1000L)), 
     new Date(dref.getTime() + (24L * 60L * 60L * 1000L)) 
    }; 
    String[] refs = new String[ds.length]; 
    for (int i = 0; i < ds.length; ++i) { 
     // How the corresponding ds[i] should be formatted 
     refs[i] = dateFormat.format(ds[i]); 
    } 

    for (int i = 0; i < 100; i++) { 
     // Every even numbered thread uses ds[0] and refs[0], 
     // odd numbered threads use ds[1] and refs[1]. 
     int index = (i % 2); 
     final Date d = ds[index]; 
     final String ref = refs[index]; 
     new Thread(new Runnable() { 
     @Override 
     public void run() { 
      while (true) { 
      String s = dateFormat.format(d); 
      if (!ref.equals(s)) { 
       throw new IllegalStateException("Expected: " + ref + ", got: " + s); 
      } 
      } 
     } 
     }).start(); 
    } 
    } 
} 

Как показывают комментарии, каждый четный поток будет форматировать вчерашнюю дату, и нечетные потоки будут использовать завтрашние даты.

Если вы запустите это, потоки в значительной степени сразу начнут совершать самоубийство, бросив исключения до тех пор, пока у вас останется только несколько левых, вероятно, все форматирование в ту же дату.

Смежные вопросы