Вместо этого синхронизируется с фиктивным объектом

Я встретил код как несколько раз

class Foo {
   private Object lock = new Object();

   public void doSomething() {
      synchronized(lock) {
         ...

Что меня интересует, почему создается объект блокировки вместо записи synchronized(this)? Нужно ли разрешать совместное использование блокировки? Я смутно помню, что читал, что это оптимизация. Это правда? Кроме того, имеет ли смысл в некотором контексте иметь блокировку, объявленную как final?

Ответ 1

  • Синхронизация на this обескуражена, потому что, если сам объект используется как блокировка извне, он нарушит внутреннюю синхронизацию. Кроме того, помните, что методы synchronized также используют this как блокировку, что может вызвать нежелательный эффект.
  • Объявление блокировки final рекомендуется для предотвращения ситуации, в которой объект блокировки переназначается внутри блока synchronized, тем самым заставляя разные потоки видеть разные объекты блокировки. См. Другой вопрос: Блокировка на изменяемом объекте - Почему это считается плохой практикой?

Ответ 2

Представьте сценарий, в котором у вас есть thread1 и thread2 доступ method1, thread3 и thread4 доступ method2. Синхронизация на this будет блокировать thread3 и thread4, если thread1 или thread2 обращаются к method1, которые не должны происходить, поскольку thread3 и thread4 не имеют ничего общего с method1. Оптимизация для этого заключается в использовании двух разных блокировок вместо блокировки всего экземпляра класса.

Вот хороший параграф, который я нашел на JavaDoc, который отражает это:

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

Ответ 3

Рассмотрим использование блокировки из пакета java.util.concurrent
Это может обеспечить более оптимизированную блокировку, например, с помощью ReadWriteLock позволит всем читателям получить доступ, не дожидаясь ожидания.

Ответ 4

Вот полный рабочий пример "ReentrantReadWriteLock".

 public class DataReader  {
 private static final ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock();
 private static final Lock read = readWriteLock.readLock();
 private static final Lock write = readWriteLock.writeLock();
 public static void loadData() {
  try {
   write.lock();
   loadDataFromTheDB());
  } finally {
   write.unlock();
  }
 }

 public static boolean readData() {
  try {
   read.lock();
   readData();
  } finally {
   read.unlock();
  }
 }

 public static List loadDataFromTheDB() {
     List list = new ArrayList();
  return list;
 }

    public static void readData() throws DataServicesException {
     //Write reading logic
    }
}