2013-04-26 31 views
7
private volatile Object obj = new MyObject(); 
void foo() 
{ 
    synchronized(obj) 
    { 
    obj.doWork(); 
    } 
} 
void bar() 
{ 
    synchronized(obj) 
    { 
    obj.doWork(); 
    obj = new MyObject(); // <<<< notice this line (call it line-x) 
    } 
} 

Giả sử tại một điểm nhất định trong thời gian, một sợi t_bar đang thực hiện bar(), và một số khác t_foo đang thực hiện foo, và rằng t_bar vừa mua obj, vì vậy t_foo, trong thực tế, đang chờ đợi.đồng bộ hóa trên đối tượng phi chính thức

Sau khi khối đồng bộ hóa trong bar được thực thi, foo sẽ nhận được để thực thi khối đồng bộ hóa của nó, phải không? Giá trị của obj nó sẽ thấy? Cái cũ? Hoặc bộ mới được đặt trong bar?

(Tôi sẽ hy vọng rằng giá trị mới được nhìn thấy, đó là toàn bộ điểm của mã hóa nó theo cách đó, nhưng tôi muốn biết nếu điều này là một 'an toàn' đặt cược)

+3

@javapirate: Tôi tìm thấy nó RẤT thô lỗ mà bạn đã chỉnh sửa bài viết của tôi đơn giản chỉ vì bạn thích phong cách định dạng K & R của riêng bạn? Tôi xin lỗi, nhưng tôi sẽ phải định dạng lại nó. –

+0

Xin lỗi. Tôi nghĩ đến việc thoát khỏi rất nhiều không gian trống. Tiếp tục! –

+0

Một sự khác biệt có thể giúp hiểu là * đối tượng * hoặc không phải là cuối cùng hoặc không phải là cuối cùng, biến chứa tham chiếu đến chúng. Khóa nằm trên vật thể, không phải trên biến. –

Trả lời

0

này là không an toàn và phá vỡ . Thay đổi đối tượng bạn khóa không hoạt động.

Khi một chủ đề cố gắng nhập khối đã đồng bộ, trước tiên nó phải đánh giá biểu thức trong các lần parens để tìm ra khóa nào cần. Nếu khóa thay đổi sau đó, chủ đề không có bất kỳ cách nào để biết rằng, nó cuối cùng mua lại khóa cũ và đi vào khối đồng bộ. Tại thời điểm đó, nó thấy đối tượng và đánh giá rằng, lấy tham chiếu mới và gọi phương thức trên đó bằng khóa cũ (không liên quan) và không giữ khóa mới, mặc dù một số luồng khác có thể có khóa mới được giữ và có thể thực thi phương thức trên cùng một đối tượng đồng thời.

1

Nó sẽ hoạt động bình thường như thể tham chiếu đối tượng không thay đổi trong nội bộ. Lý do là thử nghiệm cho khóa trên đối tượng sẽ được thực hiện chỉ một lần. Vì vậy, ngay cả khi đối tượng thay đổi trong nội bộ, chủ đề sẽ tiếp tục chờ đợi và hành vi sẽ remian giống như thể đối tượng là giống nhau [không thay đổi].

Tôi đã thử một thứ khác. Tôi đặt một lệnh ngủ ngay sau khi đối tượng mới được tạo ra và sau đó bắt đầu luồng tiếp theo và như mong đợi cả hai luồng đều bắt đầu hoạt động đồng thời. Xem mã bên dưới.

public class ChangeLockObjectState { 

    private volatile Object obj = new Object(); 

    void foo() { 
     synchronized (obj) { 
      try { 
       System.out.println("inside foo"); 
       Thread.sleep(10000); 
      } catch (InterruptedException e) { 
       // TODO Auto-generated catch block 
       e.printStackTrace(); 
      } 
     } 
    } 

    void bar() { 
     synchronized (obj) { 
      try { 
       System.out.println("inside bar"); 
       Thread.sleep(5000); 
      } catch (InterruptedException e) { 
       // TODO Auto-generated catch block 
       e.printStackTrace(); 
      } 

      obj = new Object(); // <<<< notice this line (call it line-x) 

      System.out.println("going out of bar"); 

      try { 

       Thread.sleep(5000); 
      } catch (InterruptedException e) { 
       // TODO Auto-generated catch block 
       e.printStackTrace(); 
      } 

      System.out.println("wait over"); 

     } 
    } 

    /** 
    * @param args 
    * @throws InterruptedException 
    */ 
    public static void main(String[] args) throws InterruptedException { 
     final ChangeLockObjectState test = new ChangeLockObjectState(); 

     new Thread(new Runnable() { 

      @Override 
      public void run() { 
       test.bar(); 

      } 
     }).start(); 

     Thread.sleep(6000); 

     new Thread(new Runnable() { 

      @Override 
      public void run() { 
       test.foo(); 

      } 
     }).start(); 

    } 

} 
-2

Giá trị mới của obj sẽ được đọc.

Từ phần của tiêu chuẩn trên Happens before:

Một ghi vào một lĩnh vực dễ bay hơi (§8.3.1.4) xảy ra-trước mỗi đọc tiếp theo của lĩnh vực đó.

Từ định nghĩa của biến chia sẻ:

Tất cả các instance fields, ruộng tĩnh, và mảng yếu tố được lưu trữ trong bộ nhớ heap. Trong chương này, chúng tôi sử dụng biến số để chỉ cả hai trường và phần tử mảng. Biến cục bộ (§14.4), các tham số phương thức chính thức (§8.4.1) và các tham số xử lý ngoại lệ (§14.20) không bao giờ được chia sẻ giữa các luồng và không bị ảnh hưởng bởi mô hình bộ nhớ.

Đọc của obj bên trong khối được đồng bộ tách biệt với đánh giá ban đầu của biểu thức obj để xác định màn hình tích hợp của đối tượng nào để khóa. Việc phân công lại obj sẽ xảy ra trước lần đọc đầu tiên, nhưng không xảy ra trước lần đọc thứ hai.Vì obj là trường volatile, lần đọc thứ hai phải xem giá trị được cập nhật là obj.

+0

cách liên kết với câu hỏi này? – Lokesh

+0

Rất tiếc, bạn đã đúng. Đã trả lời câu hỏi sai :-( –

+0

Đã thay đổi để trả lời cho câu hỏi chính xác –

0

Giá trị mới được hiển thị. Và nó hoạt động ngay cả khi không thực hiện objvolatile. Đó là bởi vì sự đồng bộ hóa vẫn còn giữ trên đối tượng cũ và cung cấp khả năng hiển thị cho giá trị mới sau khi thread chờ đợi (t_foo) vào bên trong. Đây là thử nghiệm:

public class Main3 { 
    private MyObject obj = new MyObject(1); 

    void foo() 
    { 
     synchronized(obj) 
     { 
      System.out.println(obj.number); 
      obj.doWork(); 
     } 
    } 

    void bar() 
    { 
     synchronized(obj) 
     { 
      System.out.println(obj.number); 

      obj.doWork(); 

      //force the foo thread to wait at the synchronization point 
      for(int i = 0; i < 1000000000l; i++); 

      obj = new MyObject(2); // <<<< notice this line (call it line-x) 
     } 
    } 

    public static void main(String[] args) throws InterruptedException { 
     final Main3 m3 = new Main3(); 

     Thread t1 = new Thread(new Runnable() { 
      @Override 
      public void run() { 
       m3.bar(); 
      } 
     }); 

     Thread t2 = new Thread(new Runnable() { 
      @Override 
      public void run() { 
       m3.foo(); 
      } 
     }); 

     t1.start(); 
     t2.start(); 
    } 
} 

class MyObject { 
    int number; 

    public MyObject(int number) { 
     this.number = number; 
    } 

    public void doWork() { 
    } 
} 
2

Trong tình hình chính xác mà bạn mô tả, có, đọc của obj bên trong khối đồng bộ foo sẽ thấy giá trị mới được thiết lập bởi khối đồng bộ thanh trước.

Phần thú vị là, nó không phải lúc nào cũng xảy ra trong tình huống chính xác đó. Chương trình không phải là chủ đề an toàn, ví dụ, nếu ngay sau khi thoát khỏi bar(), cùng một luồng sẽ gọi một số khác bar(), trong khi chuỗi foo đang khóa trên đối tượng cũ. Chuỗi thanh khóa trên đối tượng mới, do đó, hai luồng đang thực hiện đồng thời, cả hai thực thi obj.doWork() trên cùng một obj mới.

Chúng ta có thể có một phần khắc phục nó bằng cách

// suppose this line happens-before foo()/bar() calls 
MyObject obj = new MyObject(); 

void foo() 
    while(true) 
     MyObject tmp1 = obj; 
     synchronized(tmp1) 
      MyObject tmp2 = obj; 
      if(tmp2==tmp1) 
       tmp2.doWork(); 
       return; 
      // else retry 

này ít nhất đảm bảo không có lời gọi hiện tại của obj.doWork() trên obj cùng, vì obj.doWork() chỉ có thể xảy ra trong một khối đồng bộ có thể khóa chính xác cùng obj

+0

Làm thế nào để 'foo' vẫn khóa đối tượng' cũ'? Sau khi thoát khỏi 'bar',' obj' sẽ có giá trị mới ở mọi nơi, phải không? –

+0

@ Một Không, foo sẽ vẫn giữ một khóa cho đối tượng cũ. Xem http://docs.oracle.com/javase/specs/jls/se7/html/jls-14.html#jls-14.19 hoặc xem câu trả lời cho câu hỏi của tôi 2 chỉnh sửa trước (nơi tôi trả lời * rằng * câu hỏi. –

+0

Ok, nó giữ khóa cho đối tượng cũ, mà tôi hiểu. Nhưng nó trông giống như bất kỳ tài liệu tham khảo * bên trong * khối đồng bộ sẽ đề cập đến mới giá trị ob 'obj' ...? –

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