﻿# V6070\. Unsafe synchronization on an object\.

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

Проблема заключается в том, что если производить синхронизацию по:

* 'this',
* объектам целочисленных классов оберток \(Byte, Short, Integer, Long\),
* объекту класса обертки для логического типа \(Boolean\),
* объекту класса String,

то это может приводить к потенциальным тупикам и недетерминированному поведению\.

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

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

Приведём синтетический пример взаимоблокировки при синхронизации по 'this':

```cpp
class SynchroThis
{
  void doSmt()
  {
    synchronized(this)
    {
      // do smt
    }
  }
}

....
SynchroThis obj = new SynchroThis();
synchronized(obj)
{
  Thread t = new Thread(() -> obj.doSmt());
  t.start();
  t.join();
}
....
```

В результате программа никогда не завершится, т\.к\. происходит deadlock по экземпляру класса SynchroThis \(первая блокировка в основном потоке по 'obj', вторая \- в потоке 't' по 'this'\)\.

Для того, чтобы избежать возможных взаимных блокировок, в качестве объекта блокировки стоит использовать, например, приватное поле:

```cpp
class A
{
  private final Object lock = new Object();
  void foo()
  {
    synchronized(lock)
    {
      // do smt
    }
  }
}
```

Рассмотрим синтетический пример синхронизации по объекту типа Byte: 

```cpp
class FirstClass
{
  private final Byte idLock;
  ....
  public FirstClass(Byte id, ....)
  {
    idLock = id;
    ....
  }
  ....
  public void calculateFromFirst(....)
  {
    synchronized (idLock)  // <=
    {
      ....
    }
  }
}

class SecondClass
{
  private final Byte idLock;
  ....
  public SecondClass(Byte id, ....)
  {
    idLock = id;
    ....
  }
  ....
  public void calculateFromSecond(....)
  {
    synchronized (idLock)  // <=
    {
      ....
    }
  }
}
```

Обусловим, что поток N1 оперирует объектом класса 'FirstClass', а поток N2 \- 'SecondClass'\. 

Теперь давайте рассмотрим сценарий:

* У объекта класса 'FirstClass' поле 'idLock' равно 100, у объекта класса 'SecondClass' тоже 100;
* Поток N1 начинает выполнять метод 'calculateFromFirst', и какое\-то время выполняется;
* Поток N2 \(сразу же следом\) начинает выполнять метод 'calculateFromSecond'\.

Итак, у нас 2 разных потока выполняют совершенно разную логику программы для разных объектов\.  Что же получится? А получится то, что поток N2 будет находиться в состоянии ожидания до тех пор, пока поток N1 не закончит работу в синхронизированном блоке по объекту 'idLock'\. Почему же так получается?

Как и все объекты, переменные созданные с помощью классов оберток будут храниться в куче\. У каждого такого объекта будет свой адрес в куче\. Но есть небольшой нюанс, который нужно всегда учитывать\. Целочисленные классы обертки, полученные при помощи автоупаковки, со значением в диапазоне \[\-128\.\.127\] кэшируются JVM\. Поэтому такие обертки с одинаковыми значениями в этом диапазоне будут являться ссылками на один объект\.

Так и получается в нашем случае\. Синхронизация производится по одному и тому же объекту в памяти, чего вовсе и не ожидалось\.

Также, помимо целочисленных классов оберток, не следует использовать для синхронизации объекты классов:

* Boolean;
* String \(так как может получиться, что синхронизация будет производиться по строке, которая хранится в пуле строк\)\.

Использование синхронизации по таким объектам небезопасно\. Рекомендуется использовать вышеописанный способ с приватным полем, но если по каким\-либо причинам Вам это не подходит, то создавайте объекты явно при помощи конструктора\. Такой способ гарантирует, что у объектов будут разные адреса\. Пример безопасного кода:

```cpp
class FirstClass
{
  private final Byte idLock;
  ....
  public FirstClass(Byte id, ....)
  {
    idLock = new Byte(id);
    ....
  }
  ....
  public void calculateFromFirst(....)
  {
    synchronized (idLock)
    {
      ....
    }
  }
}
....
```

Дополнительную информацию можно посмотреть [здесь](https://wiki.sei.cmu.edu/confluence/display/java/LCK01-J.+Do+not+synchronize+on+objects+that+may+be+reused)\.