2012-06-25 45 views
5

Tôi đang nhầm lẫn về việc đồng bộ hóa một phương thức thể hiện và một phương thức tĩnh. Tôi muốn viết một sợi lớp an toàn như sau:Đồng bộ hóa trên phương pháp tĩnh và ví dụ

public class safe { 

    private final static ConcurrentLinkedQueue<Object> objectList= 
     new ConcurrentLinkedQueue<Object>(); 

    /** 
    * retrieves the head of the object and prints it 
    */ 
    public synchronized static void getHeadObject() { 
     System.out.println(objectList.peek().toString()); 

    } 

    /** 
    * creates a new object and stores in the list. 
    */ 
    public synchronized void addObject() { 
     Object obj=new Object(); 
     objectList.add(obj); 

    } 
} 

Đồng bộ hóa trên một phương pháp tĩnh sẽ khóa trên safe.class khóa và đồng bộ hóa trên một phương pháp dụ sẽ khóa trên .và này vì thế không ổn định sẽ đạt được .

Nếu tôi muốn đạt được trạng thái nhất quán cho đoạn mã dưới đây, làm cách nào để đạt được điều đó?

+1

Tại sao 'addObject' một phương pháp dụ? Tại sao không tĩnh? Tại sao bạn đồng bộ hóa xung quanh một đối tượng đồng thời? Chúng đã được an toàn. – Wug

+0

Bạn có ý nghĩa gì bởi trạng thái không nhất quán? –

+0

Tôi giả định anh ta có nghĩa là 'Queue objectList = new List ' thay vì 'ConcurrentLinkedQueue objectList = new ConcurrentLinkedQueue ' vì vậy chúng tôi nhận được câu trả lời cho câu hỏi thực tế mà anh ta đang yêu cầu. –

Trả lời

1

EDIT: Tôi giả sử bạn có nghĩa là Queue<Object> objectList thay vì ConcurrentLinkedQueue<Object> objectList. ConcurrentLinkedQueue<Object> đã thực hiện tất cả các chủ đề an toàn cho bạn, nghĩa là bạn có thể gọi objectList.peek() tất cả những gì bạn muốn mà không phải lo lắng về điều kiện chủng tộc. Điều này là tuyệt vời nếu bạn đang phát triển các chương trình đa luồng nhưng không quá tuyệt vời cho việc học về an toàn luồng.

Phương pháp của bạn không cần phải là synchronized, giả sử bạn có một chuỗi hoạt động trên một cá thể của đối tượng tại một thời điểm, tuy nhiên nếu bạn cần có nhiều phiên bản của tất cả tham chiếu đến cùng một biến lớp tĩnh, bạn cần phải synchronized qua biến lớp học như vậy:

public static void getHeadObject() { 
    synchronized(safe.objectList) { 
     System.out.println(objectList.peek().toString()); 
    } 
} 

này khóa objectList và không cho phép nó để được đọc hoặc ghi vào trong bất kỳ chủ đề khác ngay sau khi chương trình là bên trong khối đồng bộ hóa. Làm tương tự cho tất cả các phương thức khác là synchronized.

LƯU Ý:

Tuy nhiên, kể từ khi bạn đang làm chỉ có một đơn giản get hoạt động List.peek(), bạn thực sự không cần phải đồng bộ hóa qua objectList vì trong một điều kiện chủng tộc, nó sẽ nhận được một trong hai giá trị List hoặc cách khác. Vấn đề với điều kiện chủng tộc là khi nhiều hoạt động đọc/ghi phức tạp được thực hiện, với giá trị thay đổi ở giữa chúng.

Ví dụ, nếu bạn đã có một lớp PairInt với một PairInt.xPairInt.y lĩnh vực, với ràng buộc là x = 2y, và bạn muốn làm

System.out.println(myIntPair.x.toString() + ", " + myIntPair.y.toString()); 

và thread khác đã được cập nhật các giá trị của xy tại cùng một lúc,

myIntPair.y = y + 3; 
myIntPair.x = 2 * y; 

Và chủ đề đã chỉnh sửa myIntPair ở giữa chủ đề đã đọc myIntPair.x.toString()myIntPair.y.toString() bạn có thể nhận được đầu ra trông giống như (10, 8), có nghĩa là nếu bạn đang hoạt động với giả định rằng x == 2 * y có thể làm hỏng chương trình của bạn.

Trong trường hợp đó, đọc của bạn cần phải sử dụng một synchronized, nhưng đối với những thứ đơn giản hơn như peek() trên một đơn giản object đang được thêm vào hoặc xóa, không thay đổi trong khi trong hàng đợi, các synchronized thể, trong hầu hết các trường hợp Bị bỏ. Thực tế, đối với string, int, bool và các loại tương tự, điều kiện synchronized để đọc đơn giản sẽ bị xóa.

Tuy nhiên, việc viết phải luôn là synchronized về các hoạt động không rõ ràng là an toàn, tức là đã được java xử lý. Và ngay khi bạn có được nhiều hơn một tài nguyên, hoặc yêu cầu tài nguyên của bạn giữ nguyên trong suốt hoạt động như bạn làm nhiều dòng logic để nó, sau đó bạn PHẢI SỬ DỤNGsynchronized

+0

Ông đã sử dụng từ khóa được đồng bộ trong khai báo 'getHeadObject', mà đơn giản là đồng bộ hóa trên safe.class. – Wug

+0

Tôi nghĩ rằng Hans là chính xác bởi vì chúng ta cần phải rất cẩn thận trong các tình huống như vậy, bất kỳ phương thức instance đồng bộ nào cũng phải có một khối đồng bộ rõ ràng để truy cập các trường tĩnh nếu các trường tĩnh yêu cầu một truy cập độc quyền. –

+0

Điều đó không giúp gì cả ... hai đối tượng an toàn sẽ truy cập cùng một objectList, và nếu bạn đang đồng bộ hóa trên cả hai đối tượng, 'sync' sẽ cho phép bạn viết với cả hai cùng một lúc, khiến bạn không sử dụng một cách rõ ràng (theo như vm có thể nói), cùng một đối tượng. –

2

Thứ nhất, ConcurrentLinkedQueue không yêu cầu rõ ràng đồng bộ hóa. Xem this answer.

Thứ hai, bạn luôn có thể đồng bộ hóa đối tượng bạn đang truy cập:

public class safe { 

     private final static ConcurrentLinkedQueue<Object> objectList= 
      new ConcurrentLinkedQueue<Object>(); 

     /** 
     * retrieves the head of the object and prints it 
     */ 
    public static void getHeadObject() { 
     synchronized(objectList){ 
      System.out.println(objectList.peek().toString()); 
     } 

    } 

     /** 
     * creates a new object and stores in the list. 
     */ 
    public void addObject() { 
      Object obj=new Object(); 
     synchronized(objectList){ 
      objectList.add(obj); 
     } 

    } 
} 
+0

@assylias: "sao chép-dán", xin lỗi =) –

0

Một vài ý kiến:

  • Java ước:
    • tên lớp phải ở trong CamelCase (tức là gọi lớp học của bạn Safe, không phải safe)
    • static có trước synchronized trong các phương pháp khai
    • static đến trước final trong việc kê khai các lĩnh vực
  • như những người khác đã nói, ConcurrentLinkedQueue đã được chủ đề an toàn, do đó không cần phải đồng bộ hóa trong ví dụ bạn đưa ra.
  • trộn các phương pháp tĩnh và không tĩnh theo cách bạn làm có vẻ lạ.
  • giả sử rằng trường hợp sử dụng thực tế của bạn phức tạp hơn và bạn cần phương pháp để chạy các hoạt động nguyên tử, sau đó mã của bạn không hoạt động, như bạn đã chỉ ra, vì 2 phương pháp đồng bộ không đồng bộ hóa trên cùng một màn hình:
public static synchronized getHeadObject(){} //monitor = Safe.class 
public static synchronized addObject(){} //monitor = this 

vì vậy, để trả lời câu hỏi cụ thể của bạn, bạn có thể sử dụng một đối tượng tĩnh riêng biệt như một khóa:

public class Safe { 

    private static final ConcurrentLinkedQueue<Object> objectList = 
      new ConcurrentLinkedQueue<Object>(); 
    // lock must be used to synchronize all the operations on objectList 
    private static final Object lock = new Object(); 

    /** 
    * retrieves the head of the object and prints it 
    */ 
    public static void getHeadObject() { 
     synchronized (lock) { 
      System.out.println(objectList.peek().toString()); 
     } 
    } 

    /** 
    * creates a new object and stores in the list. 
    */ 
    public void addObject() { 
     synchronized (lock) { 
      Object obj = new Object(); 
      objectList.add(obj); 
     } 
    } 
} 
Các vấn đề liên quan