JavaRush/Java блог/Архив info.javarush/Синхронизация потоков, блокировка объекта и блокировка кл...
CynepHy6
34 уровень

Синхронизация потоков, блокировка объекта и блокировка класса

Статья из группы Архив info.javarush
участников
Синхронизация потоков, блокировка объекта и блокировка класса - 1Синхронизация относится к многопоточности. Синхронизированый блок кода может быть выполнен только одним потоком одновременно. Java поддерживает несколько потоков для выполнения. Это может привести к тому, что два или более потока получат доступ к одному и тому же полю или объекту. Синхронизация это процесс, который позволяет выполнять все параллельные потоки в программе синхронно. Синхронизация позволяет избежать ошибок согласованности памяти, вызванные из-за непоследовательного доступа к общей памяти. Когда метод объявлен как синхронизированный — нить держит монитор для объекта, метод которого исполняется. Если другой поток выполняет синхронизированный метод, ваш поток заблокируется до тех пор, пока другой поток не отпустит монитор. Синхронизация достигается в Java использованием зарезервированного слова synchronized. Вы можете использовать его в своих классах определяя синхронизированные методы или блоки. Вы не сможете использовать synchronized в переменных или атрибутах в определении класса.

Блокировка на уровне объекта

Это механизм синхронизации не статического метода или не статического блока кода, такой, что только один поток сможет выполнить данный блок или метод на данном экземпляре класса. Это нужно делать всегда, когда необходимо сделать данные на уровне экземпляра потокобезопасными. Пример:
public class DemoClass{
    public synchronized void demoMethod(){}
}
или
public class DemoClass{
    public void demoMethod(){
        synchronized (this)        {
            //other thread safe code
        }
    }
}
или
public class DemoClass{
    private final Object lock = new Object();
    public void demoMethod(){
        synchronized (lock)        {
            //other thread safe code
        }
    }
}

Блокировка на уровне класса

Предотвращает возможность нескольким потокам войти в синхронизированный блок во время выполнения в любом из доступных экземпляров класса. Это означает, что если во время выполнения программы имеется 100 экземпляров класса DemoClass, то только один поток в это время сможет выполнить demoMethod() в любом из случаев, и все другие случаи будут заблокированы для других потоков. Это необходимо когда требуется сделать статические данные потокобезопасными.
public class DemoClass{
    public synchronized static void demoMethod(){}
}
или
public class DemoClass{
    public void demoMethod(){
        synchronized (DemoClass.class)        {
            //other thread safe code
        }
    }
}
или
public class DemoClass
{
    private final static Object lock = new Object();
    public void demoMethod(){
        synchronized (lock)        {
            //other thread safe code
        }
    }
}

Некоторые важные замечания

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

  2. synchronized можно использовать только с методами и блоками кода. Эти методы или блоки могут быть статическими или не-статическими.

  3. когда какой либо поток входит в синхронизированный метод или блок он приобретает блокировку и всякий раз, когда поток выходит из синхронизированного метода или блока JVM снимает блокировку. Блокировка снимается, даже если нить оставляет синхронизированный метод после завершения из-за каких-либо ошибок или исключений.

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

  5. Синхронизация в Java будет бросать NullPointerException если объект используемый в синхронизированном блоке null. Например, в вышеприведенном примере кода, если замок инициализируется как null, синхронизированный (lock) бросит NullPointerException.

  6. Синхронизированные методы в Java вносят дополнительные затраты на производительность вашего приложения. Так что используйте синхронизацию, когда она абсолютно необходима. Кроме того, рассмотрите вопрос об использовании синхронизированных блоков кода для синхронизации только критических секций кода.

  7. Вполне возможно, что и статический и не статический синхронизированные методы могут работать одновременно или параллельно, потому что они захватывают замок на другой объект.

  8. В соответствии со спецификацией языка вы не можете использовать synchronized в конструкторе это приведет к ошибке компиляции.

  9. Не синхронизируйте по не финальному (no final) полю, потому что ссылка, на не финальное поле может измениться в любое время, а затем другой поток может получить синхронизацию на разных объектах и уже не будет никакой синхронизации вообще. Лучше всего использовать класс String, который уже неизменяемый и финальный.

Удачи в обучении!! Оригинал: Object level lock vs Class level lock in Java
Комментарии (20)
  • популярные
  • новые
  • старые
Для того, чтобы оставить комментарий Вы должны авторизоваться
King Kazzzma 007
Уровень 45
11 июля 2023, 13:45
до того как прочитал пункт 7 замечаний я думал что понял синхронизацию, но либо там написан бред, либо о синхронизации я не понял по факту ничего....когда мы блокируем по this, то блокируются манипуляции со всеми полями класса - как со статическими так и с нестатическими для нити которая вошла в блок, а когда вызываем блокировку по классу, то блокируются только статические поля...каким образом возможны 2 блокировки одновременно, ведь тогда возможно ситуация когда статические поля будут меняться одновременно 2-мя нитями? или все же при блокировке по this блокируются только нестатические поля?
Lou Domico
Уровень 35
12 ноября 2023, 18:37
this относится к экземпляру, как и нон-статик поля. Нельзя использовать this в статическом методе или к статической переменной, потому что они принадлежат не экземпляру, а классу
King Kazzzma 007
Уровень 45
27 февраля, 12:06
и как это коррелирует с написанным мной? я и не писал что в статическом контексте можно использовать нестатический, я написал что в нестатическом методе можно использовать статический контент. То есть когда мы используем блокировку по this то можем менять и статические поля тоже, в то время как блокируя по классу мы можем менять только статические поля. Ну если я конечно все правильно понимаю. Соответственно, одновременно они работать вроде как не могут.
Maxim Belich Работает в BelEnergo
21 марта 2023, 13:12
Очень хорошие замечания в конце
🦔 Виктор веду учебный тг-канал в t.me/Javangelion Expert
23 января 2021, 13:29
Статья, конечно, архивная, но на неё до сих пор ссылаются и я считаю, что гугл-перевод блока замечаний просто непозволительный. Лучше его убрать вовсе, чем в таком виде публиковать. Вполне возможно — это, блин, что ещё такое?! Просто непозволительное выражение в учебном материале, халтура! Но, всё получится!
ANDREY TYUNIKOV
Уровень 41
25 сентября 2019, 07:24
Google перевод в важных замечаниях это, кмк, не лучший способ написания статьи.
Akira
Уровень 20
18 июля 2017, 01:56
Сейчас как раз перечитал про критические секции у Эккеля, и наткнулся на синхронизацию по полю. т.е. пример
или
public class DemoClass{
    private final Object lock = new Object();
    public void demoMethod(){
        synchronized (lock)        {
            //other thread safe code
        }
    }
}
и захотелось добавить дополнение. Надо быть аккуратнее с блокировкой подобным образом. Ибо в вот таком примере<
public class DemoClass{
    private int i = 0;
    private final Object lock1 = new Object();
    private final Object lock2 = new Object();
    public void demoMethod1(){
        synchronized (lock1)        {
            //some action with i
        }
    }
    public void demoMethod2(){
        synchronized (lock2)        {
            //some action with i
        }
    }
}
У нас в «мониторе» только lock1 и lock2, а объект класса DemoClass, чьими полями они являются-нет. Если не прав, то буду рад если поправят с пояснениями почему так, а не иначе)
CynepHy6
Уровень 34
18 июля 2017, 08:34
возможно я ничего не помню про потоки так как давно не джавист и почти год как только веб-разраб без всяких потоков и синхронизаций, но зачем лочить целый класс? сам класс ничего не делает с потоками. вся работа происходит в методах. Метод изменяет данные. И если данные надо изменять последовательно — лочим метод
Akira
Уровень 20
18 июля 2017, 14:47
Не целый класс, а объект класса) и метод имеет смысл лочить если он весь состоит из не потоко- безопасных операций с данными.(к примеру создание локальной переменной никак не повлияет на работу с объектом, и синхронизация этой операции не требуется) т.е. суть коммента была лишь одна-быть аккуратнее с блокировкой по полю объекта, с пояснением почему и только) P.S. вспомнился кстати еще один способ блокировки-с помощью объекта класса, реализующего интерфейс Lock( чтобы не лезть в дебри, к примеру класса ReentrantLock). Выглядит это вот так:
public class DemoClass{
    private Lock lock = new ReentrantLock();
    public void demoMethod(){
        lock.lock();
        try {
            //other thread safe code
        }
        finally {
        lock.unlock();
        }
    }
}
Мб. кому-то будет интересно)
Гудини
Уровень 23
18 ноября 2019, 16:41
Что значит в мониторе? Два разных локера на два разных метода. Можно наверное такое сделать если методы работают с разными полями одного объекта. В случае с одним полем это ошибочно.
Андрей
Уровень 29
Expert
30 июля 2020, 15:41
Класс можно лочить если требуется синхронизация статического метода
funner
Уровень 21
6 ноября 2016, 14:43
В дополнение к «Некоторые важные замечания»:
6. Синхронизированные методы в Java вносят дополнительные затраты на производительность вашего приложения. Так что используйте синхронизацию, когда она абсолютно необходима. Кроме того, рассмотрите вопрос об использовании синхронизированных блоков кода для синхронизации только критических секций кода.
При прочтении разнообразного материала может показаться, что синхронизация привносит затраты только в связи с блокировкой ресурса, как например пишут в одной статье с советами по многопоточному программированию на tproger.ru:
Сокращайте области синхронизации
Любой код внутри области синхронизации не может быть исполнен параллельно, и если в вашей программе 5% кода находится в блоках синхронизации, то, согласно закону Амдала, производительность всего приложения не может быть улучшена более, чем в 20 раз. Главная причина этого в том, что 5% кода всегда выполняется последовательно. Вы можете уменьшить это количество, сокращая области синхронизации – попробуйте использовать их только для критических секций. Лучший пример сокращения областей синхронизации – блокировка с двойной проверкой, которую можно реализовать в Java 1.5 и выше с помощью volatile переменных.
Однако затраты в связи с синхронизацией кроются не только здесь, сам вызов синхронизации блока несет за собой затраты процессорного времени:
<code>package my.testpackage.test06112016;

public class Main
{
    public static void main(String[] args)
    {
        int count = 1000000;

        long begin = System.currentTimeMillis();
        long end;
        for (int i = 0; i < count; i++)
        {
            synchronized (Object.class) {
                new Object();
            }
        }
        end = System.currentTimeMillis();
        System.out.println(end - begin);

        begin = System
OctoTad
Уровень 31
6 июня 2016, 16:41
Это нужно делать всегда, когда необходимо обезопасить данные экземпляра уровня нити.
А что такое уровень нити? Оригинал:
This should always be done to make instance level data thread safe.
Может стоит интерпретировать так: Это нужно делать всегда, когда необходимо сделать данные на уровне экземпляра потокобезопасными?
b_rhyme
Уровень 17
29 марта 2016, 23:45
Прекрасная статейка! Помогла понять некоторые вещи из, необъятной для меня, темы «О нитях...» и, заодно, решить задачу 17 лвл =)
Alexander Kolesnik
Уровень 35
18 июля 2020, 17:29
печально только то, что ты так и не осилил 17 левел(
voloshima
Уровень 9
22 ноября 2014, 19:10
Побольше бы примеров кода, особенно в часть Некоторые важные замечания, и особенно в п. 9. P.S. «Строковый класс» — класс String?
CynepHy6
Уровень 34
22 ноября 2014, 23:34
убрал неоднозначность
Som
Уровень 12
27 октября 2014, 02:22
Ох, какая же жесть меня ждет)))
18 мая 2020, 10:21
Судя по году оставленного сообщения и уровню - не ждет
Виктор
Уровень 1
24 сентября 2020, 07:35
может сравните мою дату регистрации с уровнем на сайте и как настоящий программист сделаете логичное заключение об моём уровне знаний? Показав тем самым - свой уровень логики.