В этом руководстве по java есть код, который показывает пример для объяснения использование ключевого слова synchronized. Я хочу сказать, почему я не должен писать что-то вроде этого:

public class MsLunch {
    private long c1 = 0;
    private long c2 = 0;
    //private Object lock1 = new Object();
    //private Object lock2 = new Object();

    public void inc1() {
        synchronized(c1) {
            c1++;
        }
    }

    public void inc2() {
         synchronized(c2) {
             c2++;
         }
     }
}

Не заморачиваясь создавать объекты блокировки? Кроме того, зачем создавать экземпляры этих объектов блокировки? Разве я не могу передать пустую ссылку? Думаю, я что-то здесь упускаю.

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

0
JoulinRouge 6 Сен 2016 в 19:30

3 ответа

Лучший ответ

Во-первых, вы не можете передавать примитивную переменную в synchronized, для этого требуется ссылка. Во-вторых, это руководство - просто пример, показывающий охраняемый блок. Он пытается защитить не c1,c2, а весь код внутри блока synchronized.

JVM использует алгоритм планирования операционной системы.

Что такое алгоритм планирования JVM?

Таким образом, JVM не несет ответственности за то, чтобы проверить, не хватает ли потоков. Однако вы можете назначить приоритет потоков, чтобы они выполнялись один над другим.

Каждый поток имеет приоритет. Потоки с более высоким приоритетом выполняются предпочтительнее потоков с более низким приоритетом. Каждый поток может быть или не быть также помечен как демон. Когда код, выполняющийся в каком-либо потоке, создает новый объект Thread, приоритет нового потока первоначально устанавливается равным приоритету создаваемого потока, и он является потоком демона тогда и только тогда, когда создающий поток является демоном.

От: https://docs.oracle.com/ javase / 7 / docs / api / java / lang / Thread.html

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

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

3
Community 23 Май 2017 в 12:24

Зачем создавать экземпляры этих объектов блокировки? Разве я не могу передать пустую ссылку?

Как уже упоминалось, вы не можете заблокировать long c1, потому что это примитив. Java блокирует монитор, связанный с экземпляром объекта. Вот почему вы также не можете заблокировать null.

руководство по потокам пытается продемонстрировать хороший шаблон, который должен создайте объекты блокировки private final, чтобы точно контролировать местоположения мьютексов, которые вы пытаетесь защитить. Вызов synchronized для this или других объектов public может привести к тому, что внешние вызывающие объекты заблокируют ваши методы, что может быть не тем, что вам нужно.

В руководстве это объясняется:

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

Таким образом, они также пытаются разрешить одновременное выполнение обновлений для c1 и c2 ("чередование") и не блокировать друг друга, в то же время обеспечивая защиту обновлений.

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

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

Если ответ утвердительный, существует ли встроенный механизм, который предотвращает прекращение работы одного метода (никогда не выполнялся или выполнялся слишком мало раз по сравнению с другим методом)?

Как уже упоминалось, это обрабатывается собственными конструкциями потоков из операционной системы. Все современные ОС обрабатывают нехватку потоков, что особенно важно, если потоки имеют разные приоритеты.

1
Gray 7 Сен 2016 в 21:27

Как ответил @ 11thdimension, вы не можете синхронизировать примитивный тип (например, длинный). Это должен быть объект класса.

Итак, у вас может возникнуть соблазн сделать что-то вроде следующего:

Long c1 = 0;
public void incC1() {
  synchronized(c1) {
    c1++;
  }
}

Это не будет работать должным образом, поскольку «c1 ++» является сокращением для «c1 = c1 + 1», которое фактически назначает новый объект для c1, и поэтому два потока могут оказаться в одном блоке синхронизированного кода.

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

Вы не можете передать пустой объект в оператор synchronized (...). Java эффективно создает семафоры для объекта ref'd и использует эту информацию для предотвращения доступа более чем одного потока к одному и тому же защищенному ресурсу.

Не всегда нужен отдельный объект блокировки, как в случае синхронизированного метода. В этом случае сам экземпляр объекта класса используется для хранения информации о блокировке, как если бы вы использовали this в методе iteslf:

public void incC1() {
    synchronized(this) {
      c1++;
    }
}
4
Jamie 6 Сен 2016 в 17:47