Khi nào tôi cần sử dụng AtomicBoolean trong Java?


Câu trả lời:


244

Khi nhiều chủ đề cần kiểm tra và thay đổi boolean. Ví dụ:

if (!initialized) {
   initialize();
   initialized = true;
}

Đây không phải là chủ đề an toàn. Bạn có thể sửa nó bằng cách sử dụng AtomicBoolean:

if (atomicInitialized.compareAndSet(false, true)) {
    initialize();
}

51
Nó không giống như một ví dụ trong thế giới thực - chủ đề khác có thể thấy truekhi initialize()chưa hoàn thành. Vì vậy, nó chỉ hoạt động nếu các chủ đề khác không quan tâm đến việc hoàn thành initialize().
axtavt

6
@axtavt: Tôi nghĩ đó là một ví dụ thực tế hoàn toàn hợp lệ nếu initializedchỉ đơn giản được sử dụng để đảm bảo rằng một và chỉ một luồng sẽ gọi initialize()phương thức. Rõ ràng initializedlà đúng không có nghĩa là việc khởi tạo chắc chắn đã hoàn thành trong trường hợp này, vì vậy có lẽ một thuật ngữ hơi khác sẽ tốt hơn ở đây. Một lần nữa, nó phụ thuộc vào những gì nó được sử dụng cho.
ColinD

14
bạn sẽ cần 2 booleans cho initStarted và initCompleted, sau đó luồng đầu tiên đặt initStarted và gọi khởi tạo (), phần còn lại đợi cho đến khi initCompleted là đúng.
Martin

3
@Bozho - đọc và ghi vào các trường boolean là nguyên tử phải không?, Bây giờ, biến động mang lại cho tôi giá trị mới nhất của trường boolean. Vì vậy, hiệu quả, sẽ không volatile booleangiống như AtomicBoolean?
TheLostMind

2
@Martin: Không có cách nào trực tiếp để chờ đợi một boolean trở thành sự thật; bạn cần cơ chế bổ sung. Cách tiếp cận hợp lý nhất là sử dụng một synchronizedkhối, trong trường hợp đó bạn không còn cần AtomicBoolean, chỉ cần a volatile boolean. ( if(! this.initialized) { synchronized(this) { if(! this.initialized) { initialize(); this.initialized = true; } } }sẽ đảm bảo rằng chỉ có một cuộc gọi luồng initializevà tất cả các luồng khác chờ nó thực hiện, với điều kiện initializedđược đánh dấu volatile.)
ruakh

52

Dưới đây là những ghi chú (từ cuốn sách Brian Goetz ) tôi đã thực hiện, điều đó có thể giúp ích cho bạn

Các lớp nguyên tử

  • cung cấp triển khai So sánh và chặn không chặn

  • Tận dụng lợi thế của sự hỗ trợ được cung cấp bởi phần cứng (hướng dẫn CMPXCHG trên Intel) Khi có nhiều luồng đang chạy qua mã của bạn sử dụng API đồng thời nguyên tử này, chúng sẽ mở rộng tốt hơn nhiều so với mã sử dụng trình giám sát / đồng bộ hóa mức đối tượng. Do các cơ chế đồng bộ hóa của Java làm cho mã chờ, khi có rất nhiều luồng chạy qua các phần quan trọng của bạn, một lượng thời gian CPU đáng kể được dành để quản lý chính cơ chế đồng bộ hóa (chờ, thông báo, v.v.). Do API mới sử dụng các cấu trúc mức phần cứng (biến nguyên tử) và chờ và khóa các thuật toán miễn phí để thực hiện an toàn luồng, nên phần lớn thời gian của CPU được dành cho "làm công cụ" thay vì quản lý đồng bộ hóa.

  • không chỉ cung cấp thông lượng tốt hơn, mà chúng còn cung cấp sức đề kháng cao hơn đối với các vấn đề sinh động như bế tắc và đảo ngược ưu tiên.


34

Có hai lý do chính tại sao bạn có thể sử dụng boolean nguyên tử. Đầu tiên, nó có thể thay đổi, bạn có thể chuyển nó dưới dạng tham chiếu và thay đổi giá trị được liên kết với chính boolean chẳng hạn.

public final class MyThreadSafeClass{

    private AtomicBoolean myBoolean = new AtomicBoolean(false);
    private SomeThreadSafeObject someObject = new SomeThreadSafeObject();

    public boolean doSomething(){
         someObject.doSomeWork(myBoolean);
         return myBoolean.get(); //will return true
    }
}

và trong lớp someObject

public final class SomeThreadSafeObject{
    public void doSomeWork(AtomicBoolean b){
        b.set(true);
    }
}

Quan trọng hơn, mặc dù, luồng của nó an toàn và có thể chỉ ra cho các nhà phát triển duy trì lớp, rằng biến này dự kiến ​​sẽ được sửa đổi và đọc từ nhiều luồng. Nếu bạn không sử dụng AtomicBoolean, bạn phải đồng bộ hóa biến boolean bạn đang sử dụng bằng cách khai báo biến động hoặc đồng bộ hóa xung quanh việc đọc và ghi của trường.


4
Đối với tình yêu của thần, điều đó chỉ thể hiện khả năng biến đổi của chính đối tượng. Tôi đặc biệt viết rằng cho mục đích trình diễn.
John Vint

Và hơn nữa, nếu đó là TẤT CẢ đã xảy ra thì có, nó sẽ luôn trở thành sự thật
John Vint

Điều đó không chứng minh rằng nó có an toàn hay không. Tôi có thể hoàn thành các đoạn mã của mình để làm cho lớp rất an toàn, nhưng điều đó chỉ giết chết quan điểm của tôi.
John Vint

1
Tôi nghĩ rằng chỉ có Volility là không đủ. Hãy suy nghĩ về một tình huống trong đó hai luồng đọc và ghi cùng một giá trị trực tiếp từ bộ nhớ chính, không có bất kỳ sự đồng bộ nào giữa các luồng đó - các vấn đề tương tranh có thể xảy ra.
Shay Tsadok

1
Bạn đúng, nó sẽ không đủ cho bộ nguyên tử sau đó kiểm tra các hoạt động, mặc dù không có đủ bối cảnh từ OP để đưa ra giả định đó. Có thể nói, biến động có thể không đủ luôn luôn đúng tùy thuộc vào tình huống của khóa học.
John Vint

18

Các AtomicBooleanlớp học cung cấp cho bạn một giá trị boolean mà bạn có thể cập nhật nguyên tử. Sử dụng nó khi bạn có nhiều luồng truy cập vào một biến boolean.

Các tổng quan về gói java.util.concurrent.atomic mang đến cho bạn một mô tả cấp cao tốt về những gì các lớp trong gói này làm gì và khi nào thì sử dụng chúng. Tôi cũng muốn giới thiệu cuốn sách Đồng thời Java trong thực hành của Brian Goetz.


5

Trích từ mô tả gói

Gói java.util.concản.atomic Mô tả: Một bộ công cụ nhỏ gồm các lớp hỗ trợ lập trình an toàn luồng không khóa trên các biến đơn. [...]

Các thông số kỹ thuật của các phương pháp này cho phép triển khai sử dụng các hướng dẫn nguyên tử ở cấp độ máy hiệu quả có sẵn trên các bộ xử lý hiện đại. [...]

Các thể hiện của các lớp AtomicBoolean, AtomicInteger, AtomicLong và AtomicReference mỗi lớp cung cấp quyền truy cập và cập nhật cho một biến duy nhất của loại tương ứng. [...]

Các hiệu ứng bộ nhớ để truy cập và cập nhật các nguyên tử thường tuân theo các quy tắc cho các chất bay hơi:

  • get có hiệu ứng bộ nhớ của việc đọc một biến dễ bay hơi.
  • set có các hiệu ứng bộ nhớ của việc viết (gán) một biến dễ bay hơi.
  • yếuCompareAndset đọc nguyên bản và ghi một cách có điều kiện một biến, được sắp xếp theo các hoạt động bộ nhớ khác trên biến đó, nhưng hoạt động như một hoạt động bộ nhớ không bay hơi thông thường.
  • so sánh và tất cả các hoạt động đọc và cập nhật khác như getAndIncrement có hiệu ứng bộ nhớ của cả đọc và viết các biến dễ bay hơi.
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.