Класс Singleton довольно распространен среди разработчиков Java, но он ставит много проблем для начинающих разработчиков. Одна из ключевых задач, стоящих перед ними, — как сохранить класс Singleton как Singleton? то есть как предотвратить множественные экземпляры Singleton по тем или иным причинам. Двойная проверка блокировки Singleton — это способ обеспечить создание только одного экземпляра класса Singleton в течение жизненного цикла приложения. Как следует из названия, при двойной проверке блокировки код проверяет существующий экземпляр класса Singleton дважды с блокировкой и без нее, чтобы гарантировать, что будет создано не более одного экземпляра singleton. Кстати, он был сломан до того, как Java исправила проблемы с моделями памяти в JDK 1.5. В этой статье мы увидим, как написать код для двойной проверки блокировки Singleton в Java , почему двойная проверка блокировки была нарушена до Java 5 и как это было исправлено. Кстати, это также важно с точки зрения собеседования, я слышал, что его попросили вручную кодировать блокировку синглтона с двойной проверкой для компаний финансового и сервисного секторов, и, поверьте мне, это сложно, пока у вас нет четкого понимания того, что ты делаешь. Вы также можете увидеть мой полный список вопросов по шаблонам проектирования Singleton, чтобы хорошо подготовиться.
Зачем вам нужна двойная проверка блокировки синглтон-класса?
Одним из распространенных сценариев, когда класс Singleton нарушает свои контракты, является многопоточность. Если вы попросите новичка написать код для шаблона проектирования Singleton , есть большая вероятность, что он придумает что-то вроде ниже:
|
1
2
3
4
5
6
7
8
|
private static Singleton _instance;public static Singleton getInstance() { if (_instance == null) { _instance = new Singleton(); } return _instance;} |
и когда вы укажете, что этот код создаст несколько экземпляров класса Singleton, если он будет вызван несколькими параллельными потоками, он, вероятно, синхронизирует весь этот метод getInstance (), как показано в нашем втором примере кода, метод getInstanceTS (). Хотя это потокобезопасный и решает проблему нескольких экземпляров, это не очень эффективно. Вы должны нести затраты на синхронизацию все время, когда вы вызываете этот метод, в то время как синхронизация необходима только в первом классе, когда создается экземпляр Singleton. Это приведет нас к двойной проверенной схеме блокировки , где блокируется только критическая часть кода. Программист назвал это двойной проверкой блокировки, потому что есть две проверки для _instance == null, одна без блокировки, а другая с блокировкой (внутри синхронизированной) блока. Вот как выглядит двойная проверка блокировки в Java:
|
01
02
03
04
05
06
07
08
09
10
|
public static Singleton getInstanceDC() { if (_instance == null) { // Single Checked synchronized (Singleton.class) { if (_instance == null) { // Double checked _instance = new Singleton(); } } } return _instance;} |

На первый взгляд, этот метод выглядит идеально, так как вам нужно заплатить цену за синхронизированный блок только один раз, но он все еще не работает, пока вы не сделаете переменную _instance изменчивой . Без модификатора volatile для другого потока в Java можно увидеть половину инициализированного состояния переменной _instance, но с переменной volatile, гарантирующей связь «происходит до», вся запись будет происходить в volatile _instance перед любым чтением переменной _instance. Это было не так до Java 5, и поэтому дважды проверенная блокировка была сломана раньше. Теперь, с гарантией «до и после» , вы можете смело предполагать, что это сработает. Кстати, это не лучший способ создания поточно-ориентированного Singleton, вы можете использовать Enum как Singleton , который обеспечивает встроенную потоковую безопасность при создании экземпляра. Другой способ заключается в использовании статического держателя рисунка.
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
|
/* * A journey to write double checked locking of Singleton class in Java. */class Singleton { private volatile static Singleton _instance; private Singleton() { // preventing Singleton object instantiation from outside } /* * 1st version: creates multiple instance if two thread access * this method simultaneously */ public static Singleton getInstance() { if (_instance == null) { _instance = new Singleton(); } return _instance; } /* * 2nd version : this definitely thread-safe and only * creates one instance of Singleton on concurrent environment * but unnecessarily expensive due to cost of synchronization * at every call. */ public static synchronized Singleton getInstanceTS() { if (_instance == null) { _instance = new Singleton(); } return _instance; } /* * 3rd version : An implementation of double checked locking of Singleton. * Intention is to minimize cost of synchronization and improve performance, * by only locking critical section of code, the code which creates instance of Singleton class. * By the way this is still broken, if we don't make _instance volatile, as another thread can * see a half initialized instance of Singleton. */ public static Singleton getInstanceDC() { if (_instance == null) { synchronized (Singleton.class) { if (_instance == null) { _instance = new Singleton(); } } } return _instance; }} |
Это все о двойной проверке блокировки класса Singleton в Java . Это один из спорных способов создания поточно-ориентированного Singleton в Java с более простыми альтернативами, доступными с точки зрения использования Enum в качестве класса Singleton. Я не предлагаю вам реализовывать ваш синглтон таким образом, так как есть много лучших способов реализовать шаблон синглтона в Java. Тем не менее, этот вопрос имеет историческое значение, а также учит, как параллелизм может внести незначительные ошибки. Как я уже говорил, это очень важно с точки зрения интервью. Потренируйтесь в написании двойной проверки блокировки класса Singleton вручную, прежде чем идти на любое интервью по Java. Это поможет вам понять ошибки кодирования, допущенные программистами Java. В связи с этим отметим, что в современный день разработки, основанной на тестировании, синглтон считается анти-паттерном из-за трудностей, с которыми он сталкивается, чтобы высмеивать его поведение, поэтому, если вы практикующий TDD, лучше избегать использования паттерна синглтона.
| Ссылка: | Двойная проверка блокировки на синглтон-классе в Java от нашего партнера JCG Явина Пола в блоге Javarevisited . |