2016-06-30 24 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+" "); 
     } 
    } 
} 

Ở đây tôi đã đồng bộ hóa trên biến ab. Và tôi đã tạo ra hai trường hợp khác nhau của lớp Kiểm tra cũng có, nhưng tôi luôn luôn nhận được đầu ra cho s1 theo sau bởi s2 hoặc ngược lại, nhưng không trộn lẫn, tại sao là như vậy? khi tôi đã tạo ra hai đối tượng riêng biệt (trong chính), do đó, hai chủ đề khác nhau, hai biến ab khác nhau, do đó, làm thế nào nó sẽ trở thành một nguồn tài nguyên được chia sẻ cho hai đối tượng khác nhau?Đồng bộ hóa chủ đề trên biến mẫu Integer

+5

B.T.W., bạn không đồng bộ hóa trên _variable_, bạn đang đồng bộ hóa trên đối tượng mà biến tham chiếu. Đó là một sự khác biệt quan trọng bởi vì một số người đã phạm sai lầm không phải realiznig mà biến có thể ám chỉ đến các đối tượng khác nhau vào những thời điểm khác nhau, và một số đã phạm sai lầm khi không nhận ra rằng cùng một đối tượng có thể được tham chiếu bởi nhiều hơn một biến. –

+0

Mã này trông giống mã kiểm tra hơn là trường hợp thực, nhưng nếu bạn muốn đồng bộ hóa trên khóa không có khóa, nhưng chỉ an toàn, [Số] (https://docs.oracle.com/javase/8/ docs/api/java/lang/Number.html), bạn có thể sử dụng [AtomicInteger] (https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/atomic/AtomicInteger.html). –

+0

@jameslarge: Tôi hiểu, những gì bạn đang nói. Tôi đã không chính xác về điều đó bởi vì vấn đề của tôi tập trung vào một số điểm khác. Dù sao, cảm ơn. :) – Santanu

Trả lời

12

TL; DR - đó là vì hợp nhất Integer tổng hợp. Đặt ab an Object (ví dụ: Object ab = new Object()) để đảm bảo rằng mỗi lần khóa của Check không ảnh hưởng đến những người khác.


Ban đầu tôi cũng bối rối. Điều thú vị là đủ, nếu bạn thay đổi

private Integer ab = 2; 

để

private Object ab = new Object(); 

đồng bộ hóa sẽ biến mất (bạn sẽ có được kết quả đầu ra khác nhau trên mỗi chạy). Quay lại với ab dưới dạng Integer, tôi đã chạy mã của bạn ở chế độ gỡ lỗi (với điểm ngắt trên dòng tên của chủ đề in) và tìm thấy nội dung sau. Đây là chủ đề đầu tiên:

First thread variables

Và đây là chủ đề thứ hai.

Second thread variables

Chú ý rằng nó thực sự là cùng một đối tượng, [email protected]. Mặc dù bạn nghĩ rằng bạn đã nhận được hai đối tượng khác nhau, cả hai trường ab trong hai trường hợp Kiểm tra đều đề cập đến cùng một đối tượng trong bộ nhớ! Do đó có, có đồng bộ hóa chính xác. Câu hỏi, sau đó, là làm thế nào nói rằng Integer ab = 2 hai lần giúp bạn cùng một đối tượng Integer trong bộ nhớ.


Bằng cách nói Integer ab = 2, bạn đang sử dụng autoboxing, mà theo đó một giá trị nguyên thủy (loại int) được tự động chuyển đổi thành các đối tượng loại tương ứng Integer. Đây là tương đương với cách gọi phương thức gọi autoboxing:

private Integer ab = Integer.valueOf(2); 

Nếu chúng ta nhìn vào Integer.valueOf, chúng tôi nhận thấy rằng nó có một hồ bơi cho các giá trị trong một phạm vi nhất định:

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

Đối với hầu hết các thiết lập thông thường, điều này sẽ bao gồm giá trị 2. Vì vậy, bạn đang nhận được cùng một giá trị Integer trong bộ nhớ khi bạn gọi phương thức này.

+0

Thật thú vị. Cảm ơn rất nhiều. :) – Santanu

+2

* "phạm vi nhất định" * là từ -128 đến 127 theo mặc định. Lưu ý rằng IntegerCache.high có thể được thay đổi. IntegerCache.low không thể. –

1

Nếu bạn có một cái nhìn tại bytecode có thể bạn sẽ thấy mã này:

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 cố gắng hộp giá trị nguyên thủy 2 phản đối bằng cách gọi #valueOf của Integer lớp. Và như chúng ta biết, valueOf thực hiện Flyweight mẫu của Integer. Do đó, cùng một đối tượng được chia sẻ giữa các cá thể.

4

Điều này đang xảy ra do Ingeter Pool khái niệm trong java.

Các số nguyên nằm trong khoảng từ -128 đến 127 (bao gồm), được sử dụng theo cách tương tự như nhóm Chuỗi.

Vì vậy, khi bạn sử dụng private Integer ab = 2;, ab được chia sẻ cho cả hai đối tượng Check.

Bạn có thể sử dụng giá trị> 128 hoặc bất kỳ loại đối tượng nào khác để mã của bạn sẽ không được đồng bộ hóa.

Bạn có thể xem các câu trả lời tại đây: Why does the behavior of the Integer constant pool change at 127? để hiểu khái niệm về Integer Pool.

+2

Nếu bạn cần các đối tượng rõ ràng rõ ràng, bạn nên tạo chúng một cách rõ ràng. Sử dụng các số nguyên lớn hơn sẽ cung cấp cho bạn các đối tượng riêng biệt trong việc triển khai hiện tại nhưng không đủ để làm như vậy. – plugwash

Các vấn đề liên quan