2013-11-04 20 views
6

xin lỗi nếu điều này cực kỳ rõ ràng hoặc đã được trả lời ở nơi khác. Tôi đã không thể tìm thấy bất cứ điều gì. Tôi có mã sau:Sự nhầm lẫn đồng bộ hóa Java

public class SimpleThread extends Thread { 
    public static Integer sharedVal = 0; 
    public SimpleThread() { 

    } 
    @Override 
    public void run() { 
     while(true) {   
      iterator(); 
     } 
    } 

    public void theSleeper() { 
     System.out.println("Thread: " + this.getId() + " is going to sleep!"); 
     try { 
      this.sleep(5000); 
     } catch(Exception e) { 

     } 
    } 

    public void iterator() { 
     synchronized(sharedVal) { 
      System.out.println("Iterating sharedVal in thread: " + this.getId()); 
      sharedVal++; 
      System.out.println(sharedVal); 
      theSleeper(); 
      System.out.println("Thread : " + getId() + " is done sleeping, trying to iterate again..."); 
     } 
    } 
} 

Tôi tạo hai trường hợp của lớp SimpleThread này và thực thi các phương thức chạy. Tôi sẽ mong đợi để xem một cái gì đó như: Chủ đề 9 incrementing ... Chủ đề 9 ngủ ... (sau 5 giây) Thread 10 incrementing .... Chủ đề 10 ngủ .... Tôi mong đợi điều này bởi vì tôi đang khóa phương thức lặp để chỉ có một luồng tại một thời điểm có thể nhập nó. Điều gì xảy ra thay vào đó là cả hai luồng đều tăng lên, và sau đó cả hai chờ 5 giây. Điều này lặp đi lặp lại mãi mãi. Tôi đang thiếu gì ở đây để tôi có được hành vi mong đợi? Cảm ơn rất nhiều!

EDIT: Tôi đã tạo một biến tĩnh công cộng mới: public static Object theLock = new Object(). Bây giờ, trong phương thức iterator, tôi đã đồng bộ hóa (theLock). Đầu ra bây giờ là nhiều như tôi mong đợi, bởi vì khóa không bao giờ thay đổi. Tuy nhiên, bây giờ chỉ có chủ đề 9 mới được đưa vào phương thức. Dường như chủ đề 10 đang đói, và không bao giờ có một lượt. Điều này có vẻ lạ. Nó không chỉ là một vài lần, nó luôn luôn chỉ thread 9 lặp đi lặp lại và ngủ. Tôi nghĩ rằng nó sẽ là 9, 10, 9, 10. Hoặc có lẽ một phân phối ngẫu nhiên như 9, 10, 10, 10, 9, 10, 9, 9, 10, v.v.

EDIT2: Tôi hiểu xảy ra bây giờ. Chủ đề 9 có khóa. Chủ đề 10 cố gắng nhập hàm, nhưng immedietly nói để chờ đợi. Sau khi hàm hoàn thành, nó vẫn có thể là Thread 9s. Chủ đề 9 sau đó reaqquires khóa, và chu kỳ tiếp tục. Cửa sổ thời gian cho thread 10 để có được lượt là rất nhỏ, và nếu nó đã có được một lần lượt nó sẽ có khả năng chết đói 9. Điều đó đang được nói, đặt yield() sau khi khối đồng bộ trong iterator() dường như không làm cho nó nhiều hơn hội chợ. Tôi đọc các ý kiến ​​trên phương pháp, và lịch trình có thể trong thực tế bỏ qua năng suất().

+3

autoboxing là ma quỷ –

+0

Hahaha, có vẻ như nó có thể là có vấn đề! :) – user2045279

Trả lời

3

Vấn đề của bạn nằm ở đây:

sharedVal++; 

dòng đó, do autoboxing chuyển thành này:

sharedVal = Integer.valueOf(sharedVal.intValue() + 1); 

tạo đối tượng Integer mới mỗi khi nó chạy, vì vậy mỗi khi khối synchronized bị lỗi, nó sẽ khóa một giá trị khác.

Sử dụng một đối tượng chuyên dụng để đồng bộ hóa:

private final static Object LOCK = new Object(); 

và sau đó thay đổi đồng bộ hóa của bạn để sử dụng synchronized (LOCK) {... để thay thế.

(Bạn cũng có thể sử dụng getClass() để khóa, nhưng cá nhân tôi không thích để lộ đối tượng khóa để thế giới công cộng)

+0

Có, cảm ơn bạn vì đã chỉ ra sai lầm của tôi. Xin vui lòng xem chỉnh sửa của tôi, cảm ơn! – user2045279

+0

Khi chuỗi 9 trở lại từ chế độ ngủ, nó sẽ nhả khóa và sau đó mua lại, để lại chuỗi 10 không có khả năng. Cơ hội duy nhất nó có là bộ đếm lịch dừng thread 9 betweet khóa, đó là khó xảy ra. Thêm một 'Thread.yield()' giữa các lần lặp lại, điều đó sẽ làm cho mọi việc trở nên công bằng hơn một chút. – Darkhogg

+0

Có, tôi thấy điều đó ngay bây giờ và thêm vào bản chỉnh sửa thứ hai của mình. Thread.yield() dường như không giúp được gì nhiều vì trình lên lịch có lẽ bỏ qua nó. Điều đó đang được nói, tôi nghĩ rằng tôi hiểu nhiều hơn bây giờ. Cảm ơn rất nhiều! – user2045279

5

Khi bạn đang tăng, bạn tạo một phiên bản mới của số nguyên và một đối tượng mới để khóa.

+0

^- - - - này. – MadConan

+1

@OP: Hãy thử lại bằng cách sử dụng thứ gì đó có thể thay đổi được, chẳng hạn như StringBuilder (KHÔNG phải StringBuffer vì StringBuffer được đồng bộ hóa.) – MadConan

+0

Tôi hoàn toàn đồng ý và tôi cảm thấy câm lặng. Tôi không biết tại sao Integer là bất biến, nhưng đó là một vấn đề khác. Vui lòng xem chỉnh sửa của tôi. – user2045279

1

Bạn đang thay đổi một cách hiệu quả các khóa mà chủ đề đang sử dụng trên mỗi iteration: ++ trên Integer tạo ra một trường hợp mới (Integer lớp là không thay đổi)

+0

Có, một đề cập khác là tốt, cảm ơn đã chỉ ra lỗi này. Vui lòng xem chỉnh sửa của tôi – user2045279

0

sharedVal thành viên dụ varible đang thay đổi khi bạn thực hiện sharedVal ++. Thay vào đó bạn có thể sử dụng để đồng bộ hóa các tuyên bố sau:

đồng bộ (SympleThread.class)