2016-08-14 19 views
5

Tôi đã viết một số mã đa luồng trong java và phương thức đồng bộ đã thay đổi biến, nhưng nó không đồng bộ mã của tôi, tôi vẫn nhận được các giá trị ngẫu nhiên. Có mã của tôi:Tại sao phương thức đồng bộ không được truy cập đồng bộ trong chương trình đa luồng này?

public class Main { 
    public static void main(String[] args) throws Exception { 
     Resource.i = 5; 
     MyThread myThread = new MyThread(); 
     myThread.setName("one"); 
     MyThread myThread2 = new MyThread(); 
     myThread.start(); 
     myThread2.start(); 
     myThread.join(); 
     myThread2.join(); 
     System.out.println(Resource.i); 
    } 
} 
class MyThread extends Thread { 
    @Override 
    public void run() { 
     synMethod(); 
    } 

    private synchronized void synMethod() { 
     int i = Resource.i; 
     if(Thread.currentThread().getName().equals("one")) { 
      Thread.yield(); 
     } 
     i++; 
     Resource.i = i; 
    } 
} 

class Resource { 
    static int i; 
} 

Đôi khi tôi nhận được 7, đôi khi 6, nhưng tôi đã đồng bộ synMethod, như tôi hiểu không có sợi nên đi ở phương pháp này trong khi một số chủ đề khác thực hiện này, vì vậy hoạt động nên nguyên tử, nhưng họ không, và tôi không thể hiểu tại sao? Bạn có thể vui lòng giải thích cho tôi và trả lời - làm thế nào tôi có thể sửa nó?

+2

gì bạn khóa các vấn đề đồng bộ. Tôi đề nghị bạn không phải là tiểu học Thread vì điều này có thể gây ra kết quả đáng ngạc nhiên. –

Trả lời

10

Thêm phương thức synchronized giống như đồng bộ hóa trên this. Vì bạn có hai trường hợp chủ đề khác nhau, chúng không khóa lẫn nhau, đồng bộ hóa này không thực sự làm gì cả.

Để đồng bộ hóa có hiệu lực, bạn nên đồng bộ hóa trên một số tài nguyên được chia sẻ. Trong ví dụ của bạn, Resource.class thể bởi một sự lựa chọn tốt:

private void synMethod() { // Not defined as synchronized 
    // Synchronization done here: 
    synchronized (Resource.class) { 
     int i = Resource.i; 
     if (Thread.currentThread().getName().equals("one")) { 
      Thread.yield(); 
     } 
     i++; 
     Resource.i = i; 
    } 
} 
+0

Quyền truy cập đọc vào Resource.i cũng phải được đồng bộ hóa để đảm bảo khả năng hiển thị thích hợp của các bản cập nhật. –

+0

@ J.B Bạn có nghĩa là, đọc 'Resource.i' _after_' join() 'ing trên cả hai chủ đề? Vâng, 'join()' tạo ra một [xảy ra-trước khi quan hệ] (https://docs.oracle.com/javase/specs/jls/se8/html/jls-17.html#jls-17.4.5) (_All các hành động trong một chủ đề xảy ra trước khi bất kỳ chuỗi nào khác trả về thành công từ một phép nối() trên chuỗi đó_), do đó phần đó là chính xác. – Roman

1

Hãy có một cái nhìn tại nghĩa về synchronized methods từ trang tài liệu oracle.

Làm cho phương pháp synchronized có hai tác dụng:

Thứ nhất, nó không phải là có thể cho hai lời gọi của phương pháp đồng bộ trên cùng một đối tượng để interleave. Khi một luồng đang thực hiện một phương thức đồng bộ cho một đối tượng, tất cả các luồng khác gọi ra các phương thức đồng bộ cho cùng một khối đối tượng (tạm dừng thực hiện) cho đến khi luồng đầu tiên được thực hiện với đối tượng.

Trở lại với câu hỏi của bạn:

synMethod() là một mức độ phương pháp đối tượng đồng bộ. Hai luồng truy cập cùng phương thức synchronized có được khóa đối tượng theo cách tuần tự. Nhưng hai luồng truy cập phương thức đồng bộ của các cá thể (đối tượng) khác nhau chạy không đồng bộ trong trường hợp không có khóa chia sẻ.

myThreadmyThread2 là hai đối tượng khác nhau => Khóa nội tại được mua trong hai đối tượng khác nhau và do đó bạn có thể truy cập các phương thức này một cách không đồng bộ.

Một giải pháp: Được trích dẫn bởi Mureinik, sử dụng đối tượng được chia sẻ để khóa.

giải pháp khác (s): Sử dụng cấu trúc đồng thời tốt hơn như ReentrantLock, vv

Bạn tìm thấy vài lựa chọn thay thế hơn trong việc có liên quan câu hỏi SE:

Avoid synchronized(this) in Java?

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