2012-07-08 37 views
9

Biết rằngSử dụng dễ bay hơi mà không cần đồng bộ hóa

Đọc và viết là atomic cho tất cả các biến khai báo biến động

Question1: này có thể được hiểu như là nếu

private volatile int x = 0; 

x++; hoạt động là nguyên tử?

Và đó

Đánh dấu biến không ổn định không loại bỏ tất cả cần phải đồng bộ hóa hành động nguyên tử, vì memory consistency errors are still possible.

question2: Tôi tự hỏi trong hoàn cảnh nào (nếu có) có thể để xem một biến được đánh dấu volatile và không thấy bất kỳ phương pháp nào của các khối được đánh dấu đồng bộ hóa (cố gắng truy cập/sửa đổi biến)?

Nói cách khác, tất cả các biến cần được bảo vệ khỏi sửa đổi đồng thời có được đánh dấu volatile không?

Trả lời

11

Biến động chỉ cung cấp cho bạn khả năng hiển thị bổ sung, ghi nguyên tử/lần đọc trong thời gian dài/đôi (nếu không được đảm bảo bởi JLS, có) và một số thứ tự bộ nhớ đảm bảo. Không đồng bộ (có thể xây dựng các khối đồng bộ bắt đầu với chỉ dễ bay hơi - Dekker's algorithm) Vì vậy, không, nó không giúp bạn với x++ - đó vẫn là đọc, viết và cần một số hình thức đồng bộ hóa.

Một ví dụ về biến động là đôi kiểm tra khóa nổi tiếng, nơi chúng ta tránh đồng bộ hóa hầu hết thời gian vì những bảo đảm trật tự là tất cả chúng ta cần:

private volatile Helper helper = null; 
public Helper getHelper() { 
    if (helper == null) { 
     synchronized(this) { 
      if (helper == null) { 
       helper = new Helper(); 
      } 
     } 
    } 
    return helper; 
} 

Một ví dụ nơi có hoàn toàn không đồng bộ có liên quan, là một lối cờ đơn giản, ở đây nó không phải về đảm bảo đặt hàng nhưng chỉ về khả năng hiển thị được bảo đảm

public volatile boolean exit = false; 
public void run() { 
    while (!exit) doStuff(); 
    // exit when exit set to true 
} 

Nếu thread khác đặt exit = true thread khác làm vòng lặp while được đảm bảo để xem t anh ta cập nhật - không có biến động, nó có thể không.

+0

Tuyệt vời câu trả lời và ví dụ. Cảm ơn bạn. – JAM

6

x ++; hoạt động là nguyên tử?

No. Điều này làm giảm xuống x = x + 1. Đọc của x là nguyên tử và ghi vào x là nguyên tử, nhưng x = x + 1 nói chung không phải là nguyên tử.

Tôi tự hỏi trong trường hợp nào (nếu có) có thể thấy biến được đánh dấu dễ bay hơi và không thấy bất kỳ phương pháp nào của khối được đánh dấu đồng bộ (cố gắng truy cập/sửa đổi biến)?

Vâng, có tất cả các loại phương pháp tiếp cận đồng thời không sử dụng synchronized. Có nhiều tiện ích khóa khác trong Java và các thuật toán không có khóa vẫn yêu cầu những thứ như volatile: ConcurrentLinkedQueue là một ví dụ cụ thể, mặc dù nó sử dụng rộng rãi các nguyên tử "ma thuật" compareAndSet.

0

Như một ví dụ nhanh chóng kiểm chứng mà có thể minh họa cho câu trả lời trước, điều này luôn luôn mang lại một số cuối cùng là 8:

import java.util.concurrent.atomic.AtomicInteger; 


public class ThreadTest_synchronize { 

public static void main(String[] args) { 

    ThreadTest_synchronize tt = new ThreadTest_synchronize(); 
    try { 
     tt.go(); 
    } catch (InterruptedException e) { 
     e.printStackTrace(); 
    } 

} 

private void go() throws InterruptedException{ 

    MyRunnable t = new MyRunnable(); 
    Thread myThread_1 = new Thread(t, "t1"); 
    Thread myThread_2 = new Thread(t, "t2"); 
    myThread_1.start(); 
    myThread_2.start(); 
    myThread_1.join(); 
    myThread_2.join(); 
    System.out.println("Processing count="+t.getCount());  

} 

private class MyRunnable implements Runnable{ 

    private AtomicInteger count=new AtomicInteger(0); 

    @Override 
    public void run() { 
     for(int i=1; i< 5; i++){ 
      doSomething(i); 
      count.getAndAdd(1); 
     }   
    } 


    public AtomicInteger getCount() { 
     return this.count; 
    } 


    private void doSomething(int i) { 
     try { 
      Thread.sleep(i*300); 
     } catch (InterruptedException e) { 
      e.printStackTrace(); 
     } 
    } 
}  

} 

trong khi điều này thường không:

public class ThreadTest_volatile { 

public static void main(String[] args) { 

    ThreadTest_volatile tt = new ThreadTest_volatile(); 
    try { 
     tt.go(); 
    } catch (InterruptedException e) { 
     e.printStackTrace(); 
    } 

} 

private void go() throws InterruptedException{ 

    MyRunnable t = new MyRunnable(); 
    Thread myThread_1 = new Thread(t, "t1"); 
    Thread myThread_2 = new Thread(t, "t2"); 
    myThread_1.start(); 
    myThread_2.start(); 
    myThread_1.join(); 
    myThread_2.join(); 
    System.out.println("Processing count="+t.getCount());  

} 

private class MyRunnable implements Runnable{ 

    private volatile int count = 0; 


    @Override 
    public void run() { 
     for(int i=1; i< 5; i++){ 
      doSomething(i); 
      count++; 
     } 

    } 

    private int add(int count){ 
     return ++count; 
    } 


    public int getCount(){ 
     return count; 
    } 

    private void doSomething(int i) { 

     try { 
      Thread.sleep(i*300); 
     } catch (InterruptedException e) { 
      e.printStackTrace(); 
     } 
    } 
} 


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