2016-07-29 4 views
4

Я столкнулся с этими вопросами пару месяцев назад, проведя опрос через Skype для немецкой компании. С учетом следующего кода:Интервью Q: Java Synchronization

private static DateFormat DATE_FORMAT = new SimpleDateFormat();  
public void doSomething() { 
    for (int i = 0; i < 100; i++) { 
     new Thread(new Runnable() { 
      @Override 
      public void run() { 
       synchronized (DATE_FORMAT) { 
        System.out.println(DATE_FORMAT.format(Calendar.getInstance().getTime())); 
       } 

      } 
     }).start(); 
    } 
} 

Состояние, если могут возникнуть проблемы с потенциальной синхронизацией и почему.

Моя интуиция говорит мне, что их не должно быть. Мы создаем 100 потоков, каждый из которых будет захватывать блокировку на одном и том же объекте (DATE_FORMAT) и отображать текущее время с большей или меньшей точностью. Тем не менее, я помню, что интервьюер упомянул что-то о несоответствиях в печати, но я не могу вспомнить правильно.

Заранее спасибо.

+2

'' System.out.println'' не гарантирован поточно, но я не вижу никакой проблемы, потому что вы «не вызывать его параллельно, а использовать синхронизированный доступ. –

+0

Вы имеете в виду, что он может печатать недействительное значение? Например, другой поток может изменить значение при печати? (Даже если это не такой сценарий) – Santi

+0

Возможно, интервьюер тянул вашу ногу ... Единственная проблема, которую я вижу (которую я даже не рассматриваю как проблему в этом случае) заключается в том, что нити не гарантируются в порядок создания. Время набирается и печатается «на лету», поэтому никакого вреда не делается. –

ответ

1

Я не вижу проблемы, так как существует критическая секция, управляемая одним монитором (DATE_FORMAT), и никаких других замков нет, поэтому нет никакого риска затвора.

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

+1

Документация для SimpleDateFormat даже говорит: Форматы даты не синхронизированы. Рекомендуется создавать отдельные экземпляры формата для каждого потока. Если несколько потоков обращаются к формату одновременно, он должен быть синхронизирован извне - это именно то, что делается здесь. –

+0

Но 'format()' не получает доступ одновременно. Блок «synchronized» предотвращает это. –

+0

Да, вот почему я сказал, что все в порядке. Извините, если мой ответ был немного неясным. –

1

В соответствии с данным кодом;

Метод форматирования вызова из экземпляра DATE_FORMAT модифицирует объект каландра, который находится в экземпляре DATE_FORMAT, поэтому его возможный 1 поток может изменять каландр перед другими отпечатками потоков (другой поток - это поток, который изменил объект календаря, но он не печать еще).

здесь ссылка в SimpleDateFormat.class

// Called from Format after creating a FieldDelegate 
private StringBuffer format(Date date, StringBuffer toAppendTo, 
          FieldDelegate delegate) { 

// Convert input date to time field list 
calendar.setTime(date); // modifies the calender's instance 

Хорошо, однако замок на DATE_FORMAT должна предотвратить любую другую тему изменить календарь внутри в DATE_FORMAT нет? - @Santi

Это не препятствует, чтобы изменить календарь непосредственно, но предотвращение доступа к экземпляру date_format, в случае, если 2 потока пытается выполнить синхронизированный блок одновременно с тем же аргументом (который является экземпляром date_format в) 1 поток должен ждать другой - выполнить этот синхронизированный блок. Вот как работает синхронизация.

Теперь, как я обещал в комментарии, я сделал симуляцию, чтобы доказать свой ответ.

private static DateFormat DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");  
    private static ArrayList<String> listDate = new ArrayList<>(); 

    public static void doSomething() 
    { 
     for (int i = 0; i < 100; i++) { 

      final long msCurrentDate = i*100; 

      new Thread(new Runnable() { 

       public void run() { 
        synchronized (DATE_FORMAT) { 
         listDate.add(DATE_FORMAT.format(new Date(msCurrentDate))); 
         //System.out.println(DATE_FORMAT.format(Calendar.getInstance().getTime())); 
        } 
       } 
      }).start(); 
     } 


     Runtime.getRuntime().addShutdownHook(new Thread() 
     { 
      @Override 
      public void run() 
      { 
       int resultSize = listDate.size(); 
       System.out.println("All elements' size :" + resultSize); 
       resultSize = listDate.stream().distinct().collect(Collectors.toList()).size(); 
       System.out.println("Unique elements' size :" + resultSize); 
      } 
     }); 


    } 

Я изменил данный код, не изменяя его цель. Как вы можете видеть, я использую фиксированное (и увеличивающееся 100 мс для потока) время для сравнения результатов с синхронизированными и несинхронизированными версиями кода.

Я печатаю даты, а также добавляю Даты в ArrayList String для работы с числами, а не просто для сравнения и сравнения.

Прежде всего позвольте мне добавить распечатанные результаты:

enter image description here

На левой стороне есть 2 множественная дата печатается, на правую стороне нет множественной Даты

конечно первых 5 результатов ничего не доказывайте, вы должны проверить каждый из них. Поэтому я добавил результаты в список и результаты печати после удаления же записей из списка

Вот результаты для синхронизированных версий:

//Output of all executions 
//All elements' size :100 
//Unique elements' size :100 

Здесь вы результаты не синхронизируются версии:

//Output of execution : 1 
//All elements' size :100 
//Unique elements' size :82 

//Output of execution : 2 
//All elements' size :100 
//Unique elements' size :78 

//Output of execution : 3 
//All elements' size :100 
//Unique elements' size :81 

Согласно результатам, мы можем сказать, что календарь изменяется по потоку X до тех пор, пока A, B, C ... нити не распечатывают дату (или добавляют к список)

Вы можете протестировать и увидеть его сами, для отличного результата вам нужен JDK 8 для использования потоков api или вы можете использовать любой другой код. Пожалуйста, дайте мне знать, если у вас есть какие-либо вопросы, чтобы мы могли спорить.

+0

Хорошо, однако блокировка на DATE_FORMAT должна предотвратить любой другой поток, чтобы изменить календарь внутри DATE_FORMAT нет? – Santi

+0

Отредактированный мой ответ У меня уже есть пример проекта для сценария, который вы ввели в комментарии, я могу загрузить или вы можете попробовать его сами, но я попытаюсь сделать пример для даты и поделиться результатами –

1

Возможно, вы должны использовать new SimpleDateFormat("HH:mm:ss.SSS") в качестве вашего форматирования, получив требуемую странность с часами и минутами, требуется хорошее время, чтобы нанести минус границу.

Я бы с уверенностью сказал, что никаких проблем с синхронизацией с кодом нет. Любые проблемы с безопасностью потоков различных вызовов помещаются в блок синхронизации, вся инициализация выглядит по порядку, никаких особых ошибок на самом деле не происходит.

Возможно, имело смысл то, что звонок Calendar.getInstance().getTime() был выполнен за пределами блока синхронизации и использован в нем. Блок синхронизации не обязательно вызовет потоки, ожидающие блокировки, в том порядке, в котором они будут поступать, что приведет к потенциальному чередованию вывода из строя, но это не относится к текущему коду. Все, что я могу предложить, это то, что, возможно, ваш интервьюер представил неправильный код, или они ошибались.

Для справки, следующий код будет производить испорченное перемежение:

public class Test { 
    private static DateFormat DATE_FORMAT = new SimpleDateFormat("HH:mm:ss.SSS"); 
    public static void main(String[] args) { 
     for (int i = 0; i < 100; i++) { 
      new Thread(new Runnable() { 
       @Override 
       public void run() { 
        Date time = Calendar.getInstance().getTime(); 
        synchronized (DATE_FORMAT) { 
         System.out.println(DATE_FORMAT.format(time)); 
        } 
       } 
      }).start(); 
     } 
    } 
} 
Смежные вопросы