В Java синхронизированный блок кода может быть выполнен только одним потоком за раз. Кроме того, Java поддерживает одновременное выполнение нескольких потоков. Это может привести к тому, что два или более потоков будут получать доступ к одним и тем же полям или объектам одновременно.
Синхронизация — это процесс, который поддерживает синхронизацию всех параллельных потоков выполнения. Синхронизация позволяет избежать ошибок согласованности памяти, вызванных несогласованным представлением разделяемой памяти. Когда метод объявлен как синхронизированный; поток удерживает объект монитора или блокировки для объекта этого метода. Если другой поток выполняет синхронизированный метод, ваш поток блокируется до тех пор, пока этот поток не освободит монитор.
Обратите внимание, что мы можем использовать ключевое слово synchronized в классе для определенных методов или блоков. Ключевое слово synchronized нельзя использовать с переменными или атрибутами в определении класса.
1. Блокировка на уровне объекта в Java
Блокировка на уровне объекта — это механизм, когда мы хотим синхронизировать нестатический метод или нестатический блок кода таким образом, чтобы только один поток мог выполнить блок кода на данном экземпляре класса. Это всегда следует делать, чтобы сделать потокобезопасными данные на уровне экземпляра.
Блокировку на уровне объекта можно выполнить следующим образом:
public class DemoClass{public synchronized void demoMethod(){}}orpublic class DemoClass{public void demoMethod(){synchronized(this){//other thread safe code}}}orpublic class DemoClass{private final Object lock = new Object();public void demoMethod(){synchronized(lock){//other thread safe code}}}
2. Блокировка на уровне класса в Java
Блокировка на уровне класса предотвращает вход нескольких потоков в синхронизированный блок в любом из всех доступных экземпляров класса во время выполнения. Это означает, что если во время выполнения есть 100 экземпляров DemoClass, то только один поток сможет выполнить demoMethod() в любом из экземпляров одновременно, а все остальные экземпляры будут заблокированы для других потоков.
Блокировка на уровне класса всегда должна выполняться, чтобы сделать статические данные потокобезопасными. Как мы знаем, ключевое слово static связывает данные методов с уровнем класса, поэтому используйте блокировку на уровне статических полей или методов, чтобы сделать это на уровне класса.
public class DemoClass{//Method is staticpublic synchronized static void demoMethod(){}}orpublic class DemoClass{public void demoMethod(){//Acquire lock on .class referencesynchronized(DemoClass.class){//other thread safe code}}}orpublic class DemoClass{private final static Object lock = new Object();public void demoMethod(){//Lock object is staticsynchronized(lock){//other thread safe code}}}
3. Блокировка на уровне объекта и блокировка на уровне класса – важные примечания
- Синхронизация в Java гарантирует, что никакие два потока не смогут одновременно или параллельно выполнить синхронизированный метод, требующий одной и той же блокировки.
- Ключевое слово synchronized может использоваться только с методами и блоками кода. Эти методы или блоки могут быть как статическими, так и нестатическими.
- Всякий раз, когда поток входит в синхронизированный метод или блок Java, он получает блокировку, и всякий раз, когда он покидает синхронизированный метод или блок, он снимает блокировку. Блокировка снимается, даже если поток покидает синхронизированный метод после завершения или из-за любой ошибки или исключения.
- Ключевое слово Java synchronized по своей природе является реентерабельным. Это означает, что если синхронизированный метод вызывает другой синхронизированный метод, которому требуется та же блокировка, то текущий поток, удерживающий блокировку, может войти в этот метод, не получая блокировку.
- Синхронизация Java выдаст NullPointerException, если объект, используемый в синхронизированном блоке, равен null. Например, в приведенном выше примере кода, если блокировка инициализирована как null, «синхронизированная(блокировка)» выдаст NullPointerException.
- Синхронизированные методы в Java снижают производительность вашего приложения. Поэтому используйте синхронизацию, когда это абсолютно необходимо. Также рассмотрите возможность использования синхронизированных блоков кода для синхронизации только критических разделов вашего кода.
- Вполне возможно, что статический синхронизированный и нестатический синхронизированный методы могут выполняться одновременно или параллельно, поскольку они блокируют разные объекты.
- Согласно спецификации языка Java, вы не можете использовать ключевое слово synchronized с конструктором. Это недопустимо и приведет к ошибке компиляции.
- Не выполняйте синхронизацию по нефинальному полю в синхронизированном блоке в Java, поскольку ссылка на нефинальное поле может измениться в любой момент, и тогда разные потоки могут синхронизироваться по разным объектам, т.е. синхронизации не будет вообще.
- Не используйте строковые литералы, поскольку на них могут ссылаться в другом месте приложения, что может привести к взаимоблокировке. Строковые объекты, созданные с помощью ключевого слова new, можно использовать безопасно. Но лучше всего создать новый экземпляр закрытого объекта ИЛИ заблокировать саму общую переменную, которую мы хотим защитить. [Спасибо Anu за указание на это в комментариях.]
Сообщите мне свои мысли и вопросы по теме «Блокировка на уровне объекта и блокировка на уровне класса в Java».