Ví dụ đơn giản và dễ hiểu nhất về từ khóa biến động trong Java


75

Tôi đang đọc về từ khóa biến động trong Java và hoàn toàn hiểu phần lý thuyết của nó.

Nhưng, những gì tôi đang tìm kiếm là, một ví dụ điển hình, cho thấy điều gì sẽ xảy ra nếu biến không biến động và nếu có.

Đoạn mã dưới đây không hoạt động như mong đợi (lấy từ đây ):

class Test extends Thread {

    boolean keepRunning = true;

    public void run() {
        while (keepRunning) {
        }

        System.out.println("Thread terminated.");
    }

    public static void main(String[] args) throws InterruptedException {
        Test t = new Test();
        t.start();
        Thread.sleep(1000);
        t.keepRunning = false;
        System.out.println("keepRunning set to false.");
    }
}

Tốt nhất, nếu keepRunningkhông dễ bay hơi , luồng sẽ tiếp tục chạy vô thời hạn. Nhưng, nó sẽ dừng lại sau vài giây.

Tôi có hai câu hỏi cơ bản:

  • Bất cứ ai có thể giải thích dễ bay hơi với ví dụ? Không phải với lý thuyết từ JLS.
  • Có thể thay thế dễ bay hơi cho đồng bộ hóa không? Nó có đạt được tính nguyên tử không?

Một bài đăng trước đây nói về nó rộng rãi stackoverflow.com/questions/7212155/java-threading-volatile
AurA,

4
Bạn đang nghĩ ngược lại. Lý tưởng nhất là nếu keepRunning không dễ bay hơi, luồng sẽ tiếp tục chạy vô thời hạn . Trên thực tế, nó là ngược lại: thêm volatile đảm bảo rằng thay đổi đối với trường sẽ hiển thị. Nếu không có từ khóa, đơn giản là không có gì đảm bảo cả, điều gì cũng có thể xảy ra; bạn không thể nói rằng luồng sẽ tiếp tục chạy [...] .
Bruno Reis,

3
Đây là vấn đề: lỗi khả năng hiển thị bộ nhớ về bản chất của chúng rất khó (không thể?) Để chứng minh bằng một ví dụ đơn giản mà lần nào cũng sẽ bị lỗi. Giả sử bạn có một máy đa lõi, ví dụ của bạn có thể sẽ bị lỗi ít nhất một vài lần nếu bạn chạy nó nhiều (giả sử 1000 lần chạy). Nếu bạn có một chương trình lớn - chẳng hạn như toàn bộ chương trình và các đối tượng của nó không vừa với bộ nhớ đệm CPU - thì điều đó sẽ làm tăng khả năng gặp lỗi. Về cơ bản, lỗi đồng thời là do nếu lý thuyết nói rằng nó có thể bị vỡ, nó có thể sẽ xảy ra, nhưng chỉ vài tháng một lần và có thể là trong quá trình sản xuất.
yshavit

Có ví dụ điển hình đã được liệt kê stackoverflow.com/questions/5816790/...
gurubelli

Đây là một ví dụ với viết lên vanijava.blogspot.co.uk/2012/01/…
Peter Lawrey

Câu trả lời:


51

Dễ bay hơi -> Khả năng hiển thị được đảm bảo và KHÔNG nguyên tử

Đồng bộ hóa (Khóa) -> Đảm bảo khả năng hiển thị và tính nguyên tử (nếu được thực hiện đúng cách)

Dễ bay hơi không thể thay thế cho đồng bộ hóa

Chỉ sử dụng biến khi bạn đang cập nhật tham chiếu và không thực hiện một số thao tác khác trên đó.

Thí dụ:

volatile int i = 0;

public void incrementI(){
   i++;
}

sẽ không an toàn cho chuỗi nếu không sử dụng đồng bộ hóa hoặc AtomicInteger vì tăng dần là một hoạt động kết hợp.

Tại sao chương trình không chạy vô thời hạn?

Điều đó phụ thuộc vào nhiều trường hợp khác nhau. Trong hầu hết các trường hợp, JVM đủ thông minh để xóa nội dung.

Cách sử dụng đúng đắn của biến động thảo luận về nhiều cách sử dụng có thể có của biến động. Sử dụng dễ bay hơi một cách chính xác rất khó, tôi sẽ nói "Khi nghi ngờ, hãy loại bỏ nó", hãy sử dụng khối đồng bộ thay thế.

Cũng thế:

khối đồng bộ có thể được sử dụng thay thế cho khối dễ bay hơi nhưng điều ngược lại là không đúng .


4
Cái này sai. dễ bay hơi đảm bảo bản chất nguyên tử. Tài liệu của Oracle chỉ rõ điều này. Xem docs.oracle.com/javase/tutorial/essential/concurrency/… .
Kalpa Gunarathna

4
Trong Java khi chúng ta có nhiều luồng, mỗi luồng có ngăn xếp riêng của nó (một không gian bộ nhớ) và init mỗi luồng có bản sao riêng của các biến mà nó có thể truy cập. Nếu từ khóa biến động không có để trang trí int i, mỗi luồng có thể sử dụng nó trong quá trình thực thi của chúng. Khi được khai báo bằng biến, mỗi luồng phải đọc / ghi giá trị của i từ / đến bộ nhớ chính trực tiếp, không phải đến / từ các bản sao cục bộ. Vì vậy, trong mỗi quan điểm luồng, các phép toán đến / từ biến i là nguyên tử.
Kalpa Gunarathna

atomicitymột phần của câu trả lời là khó hiểu. Đồng bộ hóa cung cấp cho bạn quyền truy cậpkhả năng hiển thị độc quyền lẫn nhau . volatilechỉ cho khả năng hiển thị . Đồng thời volatilethực hiện đọc / ghi cho longdoublenguyên tử (Đồng bộ hóa cũng làm điều đó bởi bản chất độc quyền lẫn nhau của nó).
IlyaEremin

27

Đối với ví dụ cụ thể của bạn: nếu không được khai báo là biến JVM của máy chủ có thể đưa keepRunningbiến ra khỏi vòng lặp vì nó không được sửa đổi trong vòng lặp (biến nó thành một vòng lặp vô hạn), nhưng JVM của máy khách thì không. Đó là lý do tại sao bạn thấy kết quả khác nhau.

Giải thích chung về các biến số biến động như sau:

Khi một trường được khai báo volatile, trình biên dịch và thời gian chạy được thông báo rằng biến này được chia sẻ và các hoạt động trên nó không được sắp xếp lại thứ tự với các hoạt động bộ nhớ khác. Các biến dễ bay hơi không được lưu trong bộ nhớ đệm trong thanh ghi hoặc trong bộ nhớ đệm nơi chúng bị ẩn khỏi các bộ xử lý khác, vì vậy việc đọc một biến dễ thay đổi luôn trả về lần ghi gần đây nhất của bất kỳ luồng nào .

Hiệu ứng khả năng hiển thị của các biến số dễ bay hơi vượt ra ngoài giá trị của chính biến số dễ bay hơi. Khi luồng A ghi vào một biến dễ thay đổi và sau đó luồng B đọc cùng một biến đó, các giá trị của tất cả các biến hiển thị cho A trước khi ghi vào biến dễ bay sẽ hiển thị cho B sau khi đọc biến dễ bay.

Việc sử dụng phổ biến nhất cho các biến biến động là cờ hoàn thành, gián đoạn hoặc trạng thái:

  volatile boolean flag;
  while (!flag)  {
     // do something untill flag is true
  }

Các biến dễ bay hơi có thể được sử dụng cho các loại thông tin trạng thái khác, nhưng cần phải cẩn thận hơn khi thực hiện điều này. Ví dụ, ngữ nghĩa của biến không đủ mạnh để làm cho hoạt động tăng dần ( count++) trở thành nguyên tử, trừ khi bạn có thể đảm bảo rằng biến chỉ được viết từ một luồng duy nhất.

Khóa có thể đảm bảo cả khả năng hiển thị và tính nguyên tử; các biến biến động chỉ có thể đảm bảo khả năng hiển thị.

Bạn chỉ có thể sử dụng các biến biến động khi đáp ứng tất cả các tiêu chí sau:

  • Việc ghi vào biến không phụ thuộc vào giá trị hiện tại của nó hoặc bạn có thể đảm bảo rằng chỉ một luồng duy nhất cập nhật giá trị;
  • Biến không tham gia bất biến với các biến trạng thái khác; và
  • Không cần khóa vì bất kỳ lý do nào khác trong khi biến đang được truy cập.

Mẹo gỡ lỗi : hãy đảm bảo luôn chỉ định -servercông tắc dòng lệnh JVM khi gọi JVM, ngay cả khi phát triển và thử nghiệm. Máy chủ JVM thực hiện tối ưu hóa nhiều hơn JVM máy khách, chẳng hạn như nâng các biến ra khỏi vòng lặp mà không được sửa đổi trong vòng lặp; mã có thể hoạt động trong môi trường phát triển (máy khách JVM) có thể bị hỏng trong môi trường triển khai (máy chủ JVM).

Đây là một đoạn trích từ "Java Concurrency in Practice" , cuốn sách hay nhất mà bạn có thể tìm thấy về chủ đề này.


15

Tôi đã sửa đổi ví dụ của bạn một chút. Bây giờ sử dụng ví dụ với keepRunning là thành viên dễ bay hơi và không bay hơi:

class TestVolatile extends Thread{
    //volatile
    boolean keepRunning = true;

    public void run() {
        long count=0;
        while (keepRunning) {
            count++;
        }

        System.out.println("Thread terminated." + count);
    }

    public static void main(String[] args) throws InterruptedException {
        TestVolatile t = new TestVolatile();
        t.start();
        Thread.sleep(1000);
        System.out.println("after sleeping in main");
        t.keepRunning = false;
        t.join();
        System.out.println("keepRunning set to " + t.keepRunning);
    }
}

Ví dụ tuyệt vời. Điều này đã hoạt động hoàn hảo ở tôi. mà không có biến động trên keepRunning luồng sẽ bị treo vĩnh viễn. Khi bạn đánh dấu keepRunningdễ bay hơi - nó sẽ dừng sau t.keepRunning = false;
Boris

4
Ví dụ làm việc cho tôi, đang tìm kiếm ví dụ làm việc. +1 vì nó đã giúp tôi và việc thiếu lời giải thích không gây hại và không đáng bị bỏ phiếu thấp.
John Doe

1
Xin chào paritosht và @John Doe, bạn có thể giúp giải thích tại sao mã của bạn là một ví dụ hoạt động không? Khi máy của tôi thực thi mã được cung cấp trong câu hỏi, có hoặc không có từ khóa biến động, nó vẫn dừng.
shanwu

Tôi nhận được cùng một kết quả với và với chúng tôi votaliteở đây
WW

13

Từ khóa biến động là gì?

từ khóa dễ bay hơi ngăn cản caching of variables.

Hãy xem xét mã, trước tiên không có từ khóa dễ bay hơi

class MyThread extends Thread {
    private boolean running = true;   //non-volatile keyword

    public void run() {
        while (running) {
            System.out.println("hello");
        }
    }

    public void shutdown() {
        running = false;
    }
}

public class Main {

    public static void main(String[] args) {
        MyThread obj = new MyThread();
        obj.start();

        Scanner input = new Scanner(System.in);
        input.nextLine(); 
        obj.shutdown();   
    }    
}

Lý tưởng nhất , chương trình này nên print hellođến khi RETURN keyđược nhấn. Nhưng trên some machinesnó có thể xảy ra rằng biến chạycachedvà bạn không thể thay đổi giá trị của nó từ shutdown () phương pháp mà kết quả trong infinitein ấn văn bản hello.

Do đó bằng cách sử dụng từ khóa dễ bay hơi, guaranteedbiến của bạn sẽ không được lưu vào bộ nhớ cache, tức là sẽ run finebật all machines.

private volatile boolean running = true;  //volatile keyword

Vì vậy, sử dụng từ khóa dễ bay hơi là a goodsafer programming practice.


7

Variable Volatile: Từ khoá dễ bay hơi có thể áp dụng cho các biến. từ khóa dễ bay hơi trong Java đảm bảo rằng giá trị của biến số dễ bay hơi sẽ luôn được đọc từ bộ nhớ chính chứ không phải từ bộ nhớ cache cục bộ của Thread.

Access_Modifier volatile DataType Variable_Name;

Trường dễ bay hơi: Một dấu hiệu cho máy ảo rằng nhiều luồng có thể cố gắng truy cập / cập nhật giá trị của trường cùng một lúc. Đối với một loại biến cá thể đặc biệt phải được chia sẻ giữa tất cả các luồng có giá trị Sửa đổi. Tương tự như biến Static (Class), Chỉ một bản sao của giá trị biến động được lưu trong bộ nhớ chính, do đó trước khi thực hiện bất kỳ Thao tác ALU nào, mỗi luồng phải đọc giá trị cập nhật từ bộ nhớ chính sau khi hoạt động ALU, nó phải ghi vào chỉ thị bộ nhớ chính. (Ghi vào biến biến động v đồng bộ hóa-với tất cả các lần đọc tiếp theo của v bởi bất kỳ luồng nào) Điều này có nghĩa là các thay đổi đối với biến biến động luôn hiển thị với các luồng khác.

nhập mô tả hình ảnh ở đây

Ở đây thành a nonvoltaile variablenếu Luồng t1 thay đổi giá trị trong bộ đệm của t1, Luồng t2 không thể truy cập giá trị đã thay đổi cho đến khi t1 ghi, t2 đọc từ bộ nhớ chính cho giá trị được sửa đổi gần đây nhất, điều này có thể dẫn đến Data-Inconsistancy.

dễ bay hơi không thể được lưu vào bộ nhớ đệm - trình lắp ráp

    +--------------+--------+-------------------------------------+
    |  Flag Name   |  Value | Interpretation                      |
    +--------------+--------+-------------------------------------+
    | ACC_VOLATILE | 0x0040 | Declared volatile; cannot be cached.|
    +--------------+--------+-------------------------------------+
    |ACC_TRANSIENT | 0x0080 | Declared transient; not written or  |
    |              |        | read by a persistent object manager.|
    +--------------+--------+-------------------------------------+

Shared Variables: Bộ nhớ có thể được chia sẻ giữa các luồng được gọi là bộ nhớ chia sẻ hoặc bộ nhớ heap. Tất cả các trường cá thể, trường tĩnh và phần tử mảng được lưu trữ trong bộ nhớ heap.

Đồng bộ hóa : đồng bộ hóa được áp dụng cho các phương thức, khối. cho phép chỉ thực thi 1 luồng tại một thời điểm trên đối tượng. Nếu t1 chiếm quyền điều khiển, thì các luồng còn lại phải đợi cho đến khi nó giải phóng điều khiển.

Thí dụ:

public class VolatileTest implements Runnable {

    private static final int MegaBytes = 10241024;

    private static final Object counterLock = new Object();
    private static int counter = 0;
    private static volatile int counter1 = 0;

    private volatile int counter2 = 0;
    private int counter3 = 0;

    @Override
    public void run() {
        for (int i = 0; i < 5; i++) {
            concurrentMethodWrong();
        }

    }

    void addInstanceVolatile() {
        synchronized (counterLock) {
            counter2 = counter2 + 1;
            System.out.println( Thread.currentThread().getName() +"\t\t « InstanceVolatile :: "+ counter2);
        }
    }

    public void concurrentMethodWrong() {
        counter = counter + 1;
        System.out.println( Thread.currentThread().getName() +" « Static :: "+ counter);
        sleepThread( 1/4 );

        counter1 = counter1 + 1;
        System.out.println( Thread.currentThread().getName() +"\t « StaticVolatile :: "+ counter1);
        sleepThread( 1/4 );

        addInstanceVolatile();
        sleepThread( 1/4 );

        counter3 = counter3 + 1;
        sleepThread( 1/4 );
        System.out.println( Thread.currentThread().getName() +"\t\t\t\t\t « Instance :: "+ counter3);
    }
    public static void main(String[] args) throws InterruptedException {
        Runtime runtime = Runtime.getRuntime();

        int availableProcessors = runtime.availableProcessors();
        System.out.println("availableProcessors :: "+availableProcessors);
        System.out.println("MAX JVM will attempt to use : "+ runtime.maxMemory() / MegaBytes );
        System.out.println("JVM totalMemory also equals to initial heap size of JVM : "+ runtime.totalMemory() / MegaBytes );
        System.out.println("Returns the amount of free memory in the JVM : "+ untime.freeMemory() / MegaBytes );
        System.out.println(" ===== ----- ===== ");

        VolatileTest volatileTest = new VolatileTest();
        Thread t1 = new Thread( volatileTest );
        t1.start();

        Thread t2 = new Thread( volatileTest );
        t2.start();

        Thread t3 = new Thread( volatileTest );
        t3.start();

        Thread t4 = new Thread( volatileTest );
        t4.start();

        Thread.sleep( 10 );;

        Thread optimizeation = new Thread() {
            @Override public void run() {
                System.out.println("Thread Start.");

                Integer appendingVal = volatileTest.counter2 + volatileTest.counter2 + volatileTest.counter2;

                System.out.println("End of Thread." + appendingVal);
            }
        };
        optimizeation.start();
    }

    public void sleepThread( long sec ) {
        try {
            Thread.sleep( sec * 1000 );
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

Static [ Class Field] vs Volatile [ Instance Field] - Cả hai đều không được lưu vào bộ nhớ đệm của chuỗi

  • Trường tĩnh là chung cho tất cả các luồng và được lưu trữ trong Vùng phương pháp. Tĩnh với chất bay hơi không sử dụng. Trường tĩnh không thể được tuần tự hóa.

  • Biến động chủ yếu được sử dụng với biến cá thể được lưu trữ trong vùng đống. Công dụng chính của biến động là duy trì giá trị cập nhật trên tất cả các Chủ đề. trường biến động trường hợp có thể được Serialized .

@xem


6

Lý tưởng nhất là nếu keepRunning không dễ bay hơi, luồng sẽ tiếp tục chạy vô thời hạn. Tuy nhiên, nó sẽ dừng lại sau vài giây.

Nếu bạn đang chạy trong một bộ xử lý đơn hoặc nếu hệ thống của bạn rất bận, hệ điều hành có thể hoán đổi các luồng, điều này gây ra một số mức độ mất hiệu lực bộ nhớ cache. Không có volatilekhông có nghĩa là bộ nhớ sẽ không được chia sẻ, nhưng JVM đang cố gắng không đồng bộ hóa bộ nhớ nếu có thể vì lý do hiệu suất nên bộ nhớ có thể không được cập nhật.

Một điều cần lưu ý nữa System.out.println(...)là đồng bộ hóa bởi vì bên dưới PrintStreamthực hiện đồng bộ hóa để dừng đầu ra chồng chéo. Vì vậy, bạn đang nhận được đồng bộ hóa bộ nhớ "miễn phí" trong chuỗi chính. Tuy nhiên, điều này vẫn không giải thích tại sao vòng lặp đọc lại thấy các bản cập nhật.

Cho dù println(...)dòng vào hay ra, chương trình của bạn vẫn xoay vòng đối với tôi theo Java6 trên MacBook Pro với Intel i7.

Bất cứ ai có thể giải thích dễ bay hơi với ví dụ? Không phải với lý thuyết từ JLS.

Tôi nghĩ rằng ví dụ của bạn là tốt. Không chắc tại sao nó không hoạt động với tất cả các System.out.println(...)câu lệnh đã bị xóa. Nó làm việc cho tôi.

Biến động có thay thế cho đồng bộ hóa không? Nó có đạt được tính nguyên tử không?

Về đồng bộ hóa bộ nhớ, volatiletạo ra các rào cản bộ nhớ giống như một synchronizedkhối ngoại trừ volatilerào cản là một hướng so với hai hướng. volatileđọc ném lên hàng rào tải trong khi viết ném lên hàng rào cửa hàng. Một synchronizedkhối là một rào cản hai hướng với việc bổ sung khóa mutex.

Về mặt atomicity, tuy nhiên, câu trả lời là "nó phụ thuộc". Nếu bạn đang đọc hoặc ghi một giá trị từ một trường thì hãy volatilecung cấp tính nguyên tử thích hợp. Tuy nhiên, việc gia tăng một volatiletrường gặp phải hạn chế ++là thực tế 3 hoạt động: đọc, tăng, ghi. Trong trường hợp đó hoặc các trường hợp mutex phức tạp hơn, synchronizedcó thể cần một khối đầy đủ . AtomicIntegergiải quyết ++vấn đề bằng một vòng quay phức tạp kiểm tra và thiết lập.


Tôi đã nhận xét cả hai câu lệnh SOPln, nhưng nó vẫn bị dừng sau vài giây .. bạn có thể chỉ cho tôi một ví dụ sẽ hoạt động như mong đợi không?
tmgr

Bạn có đang chạy trên một hệ thống xử lý duy nhất @ tm99 không? Bởi vì chương trình của bạn quay mãi mãi với tôi trên Macbook Pro Java6.
Màu xám,

Tôi đang chạy trên Win Xp 32 bit Java 6
tmgr

2
"Bất kỳ khối được đồng bộ hóa nào (hoặc bất kỳ trường biến động nào) đều khiến tất cả bộ nhớ được đồng bộ hóa" - bạn có chắc không? Bạn có cung cấp tài liệu tham khảo JLS cho nó không? Theo như tôi nhớ, đảm bảo duy nhất là các sửa đổi đối với bộ nhớ được thực hiện trước khi giải phóng khóa L1 sẽ hiển thị với các luồng sau khi chúng có được cùng một khóa L1; với chất bay hơi, tất cả các sửa đổi bộ nhớ trước khi ghi dễ bay hơi vào F1 đều hiển thị với một luồng sau khi đọc dễ bay hơi của cùng trường F1, điều này rất khác với việc nói rằng tất cả bộ nhớ * đều được đồng bộ hóa. Nó không đơn giản như bất kỳ luồng nào chạy một khối đồng bộ.
Bruno Reis,

1
Khi bất kỳ rào cản bộ nhớ nào bị vượt qua (với synchronizedhoặc volatile), sẽ có một mối quan hệ "xảy ra trước" cho tất cả bộ nhớ. Không có gì đảm bảo về thứ tự của các khóa và đồng bộ hóa trừ khi bạn khóa trên cùng một màn hình, đó là những gì bạn được gọi là @BrunoReis. Nhưng nếu println(...)hoàn thành, bạn được đảm bảo rằng keepRunningtrường được cập nhật.
Màu xám,

3

Khi có một biến volatile, nó đảm bảo rằng nó sẽ không được lưu vào bộ nhớ cache và các luồng khác nhau sẽ thấy giá trị được cập nhật. Tuy nhiên, không đánh dấu nó volatilekhông đảm bảo điều ngược lại. volatilelà một trong những điều đã bị phá vỡ trong JVM trong một thời gian dài và vẫn không phải lúc nào cũng được hiểu rõ.


Trong một @Jeff đa bộ xử lý hiện đại, nhận xét cuối cùng của bạn hơi sai / gây hiểu lầm. JVM thực sự thông minh trong việc không tăng giá trị vì làm như vậy là một cú đánh hiệu suất.
Grey,

Khi keepRunning được main đặt thành false, luồng vẫn nhìn thấy bản cập nhật vì JVM thông minh trong việc loại bỏ giá trị. Tuy nhiên, điều này không được đảm bảo (xem nhận xét từ @Gray ở trên).
Jeff Storey

2

volatilesẽ không nhất thiết phải tạo ra những thay đổi khổng lồ, tùy thuộc vào JVM và trình biên dịch. Tuy nhiên, đối với nhiều trường hợp (cạnh), nó có thể là sự khác biệt giữa việc tối ưu hóa khiến các thay đổi của một biến không được chú ý thay vì chúng được viết chính xác.

Về cơ bản, một trình tối ưu hóa có thể chọn đặt các biến không thay đổi trên thanh ghi hoặc trên ngăn xếp. Nếu một luồng khác thay đổi chúng trong heap hoặc nguyên thủy của các lớp, luồng khác sẽ tiếp tục tìm kiếm nó trên ngăn xếp và nó sẽ cũ.

volatile đảm bảo những tối ưu hóa như vậy không xảy ra và tất cả các lần đọc và ghi đều trực tiếp vào heap hoặc một nơi khác mà tất cả các luồng sẽ nhìn thấy nó.


2

Rất nhiều ví dụ tuyệt vời, nhưng tôi chỉ muốn nói thêm rằng có một số tình huống volatileđược yêu cầu nên không có một ví dụ cụ thể nào để điều chỉnh chúng a.

  1. Bạn có thể sử dụng volatileđể buộc tất cả các luồng nhận giá trị mới nhất của biến từ bộ nhớ chính.
  2. Bạn có thể sử dụng synchronizationđể bảo vệ dữ liệu quan trọng
  3. Bạn có thể sử dụng LockAPI
  4. Bạn có thể sử dụng Atomiccác biến

Kiểm tra nó để biết thêm các ví dụ về Java dễ bay hơi .


1

Vui lòng tìm giải pháp bên dưới,

Giá trị của biến này sẽ không bao giờ được lưu trong bộ nhớ cache của chuỗi cục bộ: tất cả các lần đọc và ghi sẽ đi thẳng vào "bộ nhớ chính". Biến động buộc luồng phải cập nhật biến ban đầu cho mỗi lần.

public class VolatileDemo {

    private static volatile int MY_INT = 0;

    public static void main(String[] args) {

        ChangeMaker changeMaker = new ChangeMaker();
        changeMaker.start();

        ChangeListener changeListener = new ChangeListener();
        changeListener.start();

    }

    static class ChangeMaker extends Thread {

        @Override
        public void run() {
            while (MY_INT < 5){
                System.out.println("Incrementing MY_INT "+ ++MY_INT);
                try{
                    Thread.sleep(1000);
                }catch(InterruptedException exception) {
                    exception.printStackTrace();
                }
            }
        }
    }

    static class ChangeListener extends Thread {

        int local_value = MY_INT;

        @Override
        public void run() {
            while ( MY_INT < 5){
                if( local_value!= MY_INT){
                    System.out.println("Got Change for MY_INT "+ MY_INT);
                    local_value = MY_INT;
                }
            }
        }
    }

}

Vui lòng tham khảo liên kết này http://java.dzone.com/articles/java-volatile-keyword-0 để hiểu rõ hơn về nó.


Mặc dù liên kết này có thể trả lời câu hỏi, nhưng tốt hơn hết bạn nên đưa các phần thiết yếu của câu trả lời vào đây và cung cấp liên kết để tham khảo. Các câu trả lời chỉ có liên kết có thể trở nên không hợp lệ nếu trang được liên kết thay đổi.
rút lại

Vâng, bạn hoàn toàn chính xác. Tôi sẽ thêm nó. Cảm ơn cho bình luận có giá trị của bạn.
Azhaguvel A

1

Từ khóa dễ bay hơi cho JVM biết rằng nó có thể được sửa đổi bởi một luồng khác. Mỗi luồng có ngăn xếp riêng của nó và do đó, bản sao của các biến riêng mà nó có thể truy cập. Khi một luồng được tạo, nó sẽ sao chép giá trị của tất cả các biến có thể truy cập trong bộ nhớ của chính nó.

public class VolatileTest {
    private static final Logger LOGGER = MyLoggerFactory.getSimplestLogger();

    private static volatile int MY_INT = 0;

    public static void main(String[] args) {
        new ChangeListener().start();
        new ChangeMaker().start();
    }

    static class ChangeListener extends Thread {
        @Override
        public void run() {
            int local_value = MY_INT;
            while ( local_value < 5){
                if( local_value!= MY_INT){
                    LOGGER.log(Level.INFO,"Got Change for MY_INT : {0}", MY_INT);
                     local_value= MY_INT;
                }
            }
        }
    }

    static class ChangeMaker extends Thread{
        @Override
        public void run() {

            int local_value = MY_INT;
            while (MY_INT <5){
                LOGGER.log(Level.INFO, "Incrementing MY_INT to {0}", local_value+1);
                MY_INT = ++local_value;
                try {
                    Thread.sleep(500);
                } catch (InterruptedException e) { e.printStackTrace(); }
            }
        }
    }
}

hãy thử ví dụ này có và không có biến động.


0

Các đối tượng được khai báo là dễ bay hơi thường được sử dụng để giao tiếp thông tin trạng thái giữa các luồng, Để đảm bảo bộ nhớ đệm của CPU được cập nhật, nghĩa là, được giữ đồng bộ, khi có các trường dễ bay hơi, lệnh CPU, hàng rào bộ nhớ, thường được gọi là thanh ghi nhớ hoặc hàng rào, được phát ra để cập nhật bộ đệm CPU với sự thay đổi giá trị của trường biến động.

Công cụ sửa đổi dễ thay đổi cho trình biên dịch biết rằng biến được sửa đổi bằng biến có thể được thay đổi bất ngờ bởi các phần khác trong chương trình của bạn.

Biến biến động chỉ được sử dụng trong Ngữ cảnh chủ đề. xem ví dụ ở đây

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.