2016-06-30 2 views
11
public class Main{ 
    public static void main(String[] args) throws Exception { 
     // Creating objects for class Check(2 different objects) 
     Check c = new Check("s1"); 
     Check c1 = new Check("s2"); 
     c.start();c1.start(); 
    } 
} 
class Check extends Thread{ 
    Check(String name){super(name);} 
    private Integer ab = 2; 
    public void run(){ 
     synchronized (ab) { 
      System.out.println(Thread.currentThread().getName()); 
      for(int i=0;i<10;i++)System.out.print(i+" "); 
     } 
    } 
} 

Здесь я синхронизирован по переменной ab. И я создал два разных экземпляра класса Check также, но я всегда получаю вывод для s1, за которым следует s2 или наоборот, но не смешанный, почему это так? когда я уже создал два отдельных объекта (в основном), поэтому два разных потока, две разные переменные ab, и как он становится общим ресурсом для двух разных объектов?Синхронизация потоков по переменной экземпляра Integer

+5

B.T.W., вы не синхронизируете на _variable_, вы синхронизируете объект, к которому относится переменная. Это важное различие, потому что некоторые люди допустили ошибку, не осознавая, что переменная может ссылаться на разные объекты в разное время, а некоторые ошибались, не понимая, что на один и тот же объект могут ссылаться более чем одна переменная. –

+0

Этот код больше похож на тестовый код, чем на реальный случай, но если вы хотите синхронизировать без блокировки, но потокобезопасно, [Number] (https://docs.oracle.com/javase/8/ docs/api/java/lang/Number.html), вы можете использовать [AtomicInteger] (https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/atomic/AtomicInteger.html). –

+0

@jameslarge: Я понимаю, что вы говорите. Я не был точен, потому что моя проблема была сосредоточена на каком-то другом вопросе. В любом случае, спасибо. :) – Santanu

ответ

12

TL; DR - из-за Integer объединение. Сделайте ab Object (т. Е. Object ab = new Object()), чтобы гарантировать, что каждый экземпляр блокировки Check не мешает другим.


Первоначально я тоже был озадачен. Интересно, что если изменить

private Integer ab = 2; 

в

private Object ab = new Object(); 

синхронизация уходит, (вы получите различные выходы на каждом запуске). Назад с ab как Integer, я запустил ваш код в режиме отладки (с точкой останова в строке имени потока печати) и нашел следующее. Вот первая нить:

First thread variables

А вот вторая нить.

Second thread variables

Обратите внимание, что на самом деле это тот же объект, [email protected]. Даже если вы считали, что получаете два разных объекта, оба поля ab в двух экземплярах проверки ссылаются на один и тот же объект в памяти! Поэтому да, есть правильная синхронизация. Таким образом, возникает вопрос, как Integer ab = 2 дважды получает вам тот же Integer объект в памяти.


Говоря Integer ab = 2, вы используете Autoboxing, с помощью которого элементарное значение (типа int) автоматически преобразуют в соответствующий тип объекта Integer. Это эквивалентно вызову метода Autoboxing вызова:

private Integer ab = Integer.valueOf(2); 

Если посмотреть в Integer.valueOf, мы замечаем, что у него есть бассейн для значений в пределах определенного диапазона:

public static Integer valueOf(int i) { 
    if (i >= IntegerCache.low && i <= IntegerCache.high) 
     return IntegerCache.cache[i + (-IntegerCache.low)]; 
    return new Integer(i); 
} 

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

+0

Действительно интересный. Большое спасибо. :) – Santanu

+2

* «определенный диапазон» * от -128 до 127 по умолчанию. Обратите внимание, что IntegerCache.high может быть изменен. IntegerCache.low не может. –

1

Если вы посмотрите на байткод вы, вероятно, увидеть этот код:

LINENUMBER 15 L1 
ALOAD 0 
ICONST_2 
INVOKESTATIC java/lang/Integer.valueOf (I)Ljava/lang/Integer; 
PUTFIELD com/example/Check.ab : Ljava/lang/Integer; 

Java пытается боксировать элементарное значение 2 объекта путем вызова #valueOf из Integer класса. И, как известно, IntegervalueOf реализует Flyweight модель. Таким образом, один и тот же объект делится между экземплярами.

4

Это происходит из-за Ingeter Pool Концепция в java.

Целые числа от -128 до 127 (включительно), используются так же, как и String pool.

Таким образом, если вы используете private Integer ab = 2;, то общ. Для обоих объектов Check.

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

Вы можете посмотреть ответы здесь: Why does the behavior of the Integer constant pool change at 127? для понимания концепции целочисленного пула.

+2

Если вам нужны явно разные объекты, вы должны явно их создать. Использование больших целых чисел даст вам различные объекты в текущих реализациях, но не гарантирует их выполнение. – plugwash