Java 中的 Volatile 关键字 - 说明

发布于 2024-09-16 17:43:17 字数 420 浏览 8 评论 0原文

我对我读到的有关 volatile 关键字在 java 中的应用的内容感到非常困惑。

  1. 下列说法正确的是? “对易失性字段的写入发生在同一字段的每次后续读取之前”

  2. 理想情况下何时应使用 易失性关键字?

  3. 有什么区别:

    类测试类
    { 私有 int x;
    
       同步 int get(){return x;}
       同步无效集(int x){this.x = x;}
    
    }
    

:和

class TestClass
{  private volatile int x;

   int get(){return x;}
   void set(int x){this.x = x;}

}

I am really confused about what I read about the applications of volatile keyword in java.

  1. Is the following statement correct?
    "a write to a volatile field happens before every subsequent read of the same field"

  2. Ideally when should volatile keyword used?

  3. What is the difference between:

    class TestClass
    {  private int x;
    
       synchronized int get(){return x;}
       synchronized void set(int x){this.x = x;}
    
    }
    

and

class TestClass
{  private volatile int x;

   int get(){return x;}
   void set(int x){this.x = x;}

}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

温柔嚣张 2024-09-23 17:43:17

易失性字段修饰符,而同步修饰代码块方法。因此,我们可以使用这两个关键字指定简单访问器的三种变体:

     int i1;
     int geti1() {return i1;}

     volatile int i2;
     int geti2() {return i2;}

     int i3;
     synchronized int geti3() {return i3;}

geti1() 访问当前线程中当前存储在 i1 中的值。
线程可以拥有变量的本地副本,并且数据不必与其他线程中保存的数据相同。特别是,另一个线程可能已在其线程中更新了 i1,但其中的值当前线程可能与更新后的值不同。事实上,Java 有“主”内存的概念,这是保存变量当前“正确”值的内存。线程可以拥有自己的变量数据副本,并且线程副本可以与“主”内存不同。因此,事实上,i1 的“主”内存的值为 1,thread1 的值为 2 > 对于 i1thread2,如果 thread1i1 的值为 3 > 和 thread2 都更新了 i1,但这些更新的值尚未传播到“主”内存或其他线程。

另一方面,geti2() 有效地从“主”内存访问 i2 的值。不允许易失性变量拥有与“主”内存中当前保存的值不同的变量的本地副本。实际上,声明为 volatile 的变量必须使其数据在所有线程之间同步,以便每当您在任何线程中访问或更新该变量时,所有其他线程都会立即看到相同的值。通常,易失性变量比“普通”变量具有更高的访问和更新开销。通常,允许线程拥有自己的数据副本是为了提高效率。

volile 和synchronized 之间有两个区别。

首先,synchronized 获取并释放监视器上的锁,该锁一次只能强制一个线程执行代码块。这是同步的众所周知的方面。但synchronized也同步内存。事实上,synchronized 使整个线程内存与“主”内存同步。因此,执行 geti3() 会执行以下操作:

  1. 线程获取对象 this 监视器上的锁。
  2. 线程存储器刷新其所有变量,即,其所有变量均有效地从“主”存储器读取。
  3. 执行代码块(在本例中将返回值设置为 i3 的当前值,该值可能刚刚从“主”内存重置)。
  4. (对变量的任何更改现在通常都会写入“主”内存,但对于 geti3(),我们没有任何更改。)
  5. 线程释放对象 this 监视器上的锁。

因此,其中 volatile 只同步线程内存和“主”内存之间的一个变量的值,synchronized 同步线程内存和“主”内存之间的所有变量的值,并锁定和释放一个监视器以启动。显然,同步可能比易失性有更多的开销。

http://javaexp.blogspot.com/2007/12/difference -在-挥发性-和.html之间

volatile is a field modifier, while synchronized modifies code blocks and methods. So we can specify three variations of a simple accessor using those two keywords:

     int i1;
     int geti1() {return i1;}

     volatile int i2;
     int geti2() {return i2;}

     int i3;
     synchronized int geti3() {return i3;}

geti1() accesses the value currently stored in i1 in the current thread.
Threads can have local copies of variables, and the data does not have to be the same as the data held in other threads.In particular, another thread may have updated i1 in it's thread, but the value in the current thread could be different from that updated value. In fact Java has the idea of a "main" memory, and this is the memory that holds the current "correct" value for variables. Threads can have their own copy of data for variables, and the thread copy can be different from the "main" memory. So in fact, it is possible for the "main" memory to have a value of 1 for i1, for thread1 to have a value of 2 for i1 and for thread2 to have a value of 3 for i1 if thread1 and thread2 have both updated i1 but those updated value has not yet been propagated to "main" memory or other threads.

On the other hand, geti2() effectively accesses the value of i2 from "main" memory. A volatile variable is not allowed to have a local copy of a variable that is different from the value currently held in "main" memory. Effectively, a variable declared volatile must have it's data synchronized across all threads, so that whenever you access or update the variable in any thread, all other threads immediately see the same value. Generally volatile variables have a higher access and update overhead than "plain" variables. Generally threads are allowed to have their own copy of data is for better efficiency.

There are two differences between volitile and synchronized.

Firstly synchronized obtains and releases locks on monitors which can force only one thread at a time to execute a code block. That's the fairly well known aspect to synchronized. But synchronized also synchronizes memory. In fact synchronized synchronizes the whole of thread memory with "main" memory. So executing geti3() does the following:

  1. The thread acquires the lock on the monitor for object this .
  2. The thread memory flushes all its variables, i.e. it has all of its variables effectively read from "main" memory .
  3. The code block is executed (in this case setting the return value to the current value of i3, which may have just been reset from "main" memory).
  4. (Any changes to variables would normally now be written out to "main" memory, but for geti3() we have no changes.)
  5. The thread releases the lock on the monitor for object this.

So where volatile only synchronizes the value of one variable between thread memory and "main" memory, synchronized synchronizes the value of all variables between thread memory and "main" memory, and locks and releases a monitor to boot. Clearly synchronized is likely to have more overhead than volatile.

http://javaexp.blogspot.com/2007/12/difference-between-volatile-and.html

放血 2024-09-23 17:43:17

易失性保证从变量读取的数据始终反映最新的更新值。运行时可以通过多种方式实现这一点,包括在值更改时不缓存或刷新缓存。

volatile guarantees that reads from the variable always reflects the most up to update value. The runtime can achieve this in various ways, including not caching or refreshing the cache when the value has changed.

揪着可爱 2024-09-23 17:43:17

bwawok 避开了它,但 volatile 关键字不仅仅用于内存可见性。在 Java 1.5 发布之前,volatile 关键字声明该字段将通过每次读取时命中主内存和写入时刷新来获取对象的最新值。

今天的 volatile 关键字有两件非常重要的事情:

  1. 不要担心如何,但要知道,在读取 volatile 字段时,您将始终拥有最新的值。
  2. 编译器无法对易失性读/写重新排序以维持程序顺序。

bwawok eluded to it, but the volatile keyword isnt only for memory visibility. Before Java 1.5 was released the volatile keyword declared that the field will get the most recent value of the object by hitting main memory each time for reads and flushing for writes.

Today's volatile keyword syas two very important things:

  1. Dont worry about how but know that when reading a volatile field you will always have the most up to date value.
  2. A compiler cannot re order a volatile read/write as to maintain program order.
緦唸λ蓇 2024-09-23 17:43:17

从客户端的角度来看,私有易失性字段对公共接口是隐藏的,而同步方法则更加可见。

From a client point of view, a private volatile field is hidden from the public interface while synchronized methods are more visible.

爱的那么颓废 2024-09-23 17:43:17

回答问题的第 3 部分和第 2 部分。

synchronizedvolatile 示例之间没有功能差异。

然而,每种方法在性能方面都有其自身的缺点。在某些情况下,易失性性能可能比仅使用synchronized或来自java.util.concurrent的其他原语要差。有关此问题的讨论,请参阅 -> 为什么 Java 中的变量默认情况下不是 volatile?

To answer part 3 of your question, and partly part 2.

There is no functional difference between synchronized and volatile samples.

However, each has it's own drawbacks in terms of performance. In some cases volatile performance may be really worse than just using synchronized or other primitives from java.util.concurrent. For discussion of this see -> Why aren't variables in Java volatile by default?.

枫以 2024-09-23 17:43:17

Kerem Baydoğan 的回答是完全正确的。我只想举一个实际的例子来说明 volatile 关键字为我们提供了什么。

首先,我们有一个计数器,就像

public class Counter {
    private int x;
    public int getX() { return x; }
    public void increment() { x++; }
}

一些可运行的任务,它会增加 x 的值,

@Override
public void run() {
    for (N) {
            int oldValue = counter.getX();
            counter.increment();
            int new value = counter.getX();
        }

    }
}

如果没有同步,将会有 线程之间的干扰并且根本无法解决

这个问题的最简单方法:

public class Counter {
    private int x;
    public synchronized int getX() { return x; }
    public synchronized void increment() { x++; }
}

实际上,为了强制系统中断,我做了一个Thread。在读写x之前sleep,想象一下是一个BD或者一个巨大的任务需要处理。


现在,volatile 有什么用呢?那里有很多好文章: 易失性文章这个问题< /a>

同步对公共资源的访问不是答案,但

在我们的上一篇中保持标志以停止线程是一个不错的选择。例如,假设我们想要将变量增加到 100,一个简单的方法可能是使用 易失性布尔 标志。示例:

private volatile boolean stop;

@Override
public void run() {
    while(!stop) {
        int oldValue = counter.getX();
        if (oldValue == 100) {
             stop = true;
        } else {
             counter.increment();
             int new value = counter.getX();
        }       
     }
}

这工作正常,但是,如果您从标志中删除 volatile 关键字,则可能会遇到无限循环。

Answer by Kerem Baydoğan is completely right. I just want to give an practical example about what volatile keyword offers us.

First, we have a counter, smth like

public class Counter {
    private int x;
    public int getX() { return x; }
    public void increment() { x++; }
}

And some Runnable tasks which increments the value of x

@Override
public void run() {
    for (N) {
            int oldValue = counter.getX();
            counter.increment();
            int new value = counter.getX();
        }

    }
}

With NO synchronization there is going to be interference between threads and simply is not going to work

the simplest way to solve this:

public class Counter {
    private int x;
    public synchronized int getX() { return x; }
    public synchronized void increment() { x++; }
}

Actually in order to force the system to break, I do a Thread.sleep before reading and writing x, just imagine is a BD or a huge task to deal with.


Now, what is volatile useful for? There are a lot of good articles over there: volatile article or this question

synchronizing the access to the common resource is not the answer but is a good choice to hold the flag to stop threads

I our prev. example, imagine we want to increment the variable up to 100, a simply way could be a volatile boolean flag. Example:

private volatile boolean stop;

@Override
public void run() {
    while(!stop) {
        int oldValue = counter.getX();
        if (oldValue == 100) {
             stop = true;
        } else {
             counter.increment();
             int new value = counter.getX();
        }       
     }
}

This works fine, but, if you remove the volatile keyword from the flag, it's possible to come across and infinite loop.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文