简体   繁体   English

如果2个不同的写入和读取线程永远不会同时存在,我是否需要使用volatile

[英]Do I need to use volatile, if 2 different write and read thread will never alive at the same time

By referring to http://www.javamex.com/tutorials/synchronization_volatile.shtml , I am not sure whether I need to use volatile keyword in the following case, due to additional rule 3. 通过参考http://www.javamex.com/tutorials/synchronization_volatile.shtml由于附加规则3 ,我不确定在下列情况下是否需要使用volatile关键字

  1. A primitive static variable will be write by Thread A. 原始静态变量将由线程A写入。
  2. The same primitive static variable will be read by Thread B. 线程B将读取相同的原始静态变量。
  3. Thread B will only run, after Thread A is "dead". 线程B只会在线程A“死”后运行。 ("dead" means, the last statement of Thread A's void run is finished) (“死”表示,线程A的无效运行的最后一个语句已完成)

Will the new value written by Thread A, will always committed to main memory, after it "dead"? 线程A写入的新值是否会在“死”之后始终提交给主内存? If yes, does it mean I need not volatile keyword if the above 3 conditions are meet? 如果是,是否意味着如果满足上述3个条件,我不需要volatile关键字?

I am doubt that volatile is being required in this case. 我怀疑在这种情况下需要volatile As it is required, then ArrayList may broken. 根据需要, ArrayList可能会损坏。 As one thread may perform insert and update size member variable. 由于一个线程可以执行insert和update size成员变量。 Later, another thread (not-concurrently) may read the ArrayList 's size . 之后,另一个线程(不兼容)可能会读取ArrayListsize If you look at ArrayList source code, size is not being declared as volatile. 如果查看ArrayList源代码,则不ArrayList size声明为volatile。

In JavaDoc of ArrayList , then only mention that ArrayList is not safe to be used for multiple threads access an ArrayList instance concurrently , but not for multiple threads access an ArrayList instance at different timing . ArrayList JavaDoc中,只提到ArrayList不能安全地用于多个线程同时访问ArrayList实例 ,但不能让多个线程在不同的时间访问ArrayList实例

Let me use the following code to issulate this problem 让我使用以下代码来解决此问题

public static void main(String[] args) throws InterruptedException {
    // Create and start the thread
    final ArrayList<String> list = new ArrayList<String>();
    Thread writeThread = new Thread(new Runnable() {
        public void run() {
            list.add("hello");
        }
    });
    writeThread.join();
    Thread readThread = new Thread(new Runnable() {
        public void run() {
            // Does it guarantee that list.size will always return 1, as this list
            // is manipulated by different thread?
            // Take note that, within implementation of ArrayList, member
            // variable size is not marked as volatile.
            assert(1 == list.size());
        }
    });
    readThread.join();
}

Yes, you still need to use volatile (or some other form of synchronization). 是的,您仍然需要使用volatile(或其他形式的同步)。

The reason why is that the two threads could run on different processors and even if one thread has long finished before the other starts there is no guarantee that the second thread will get the freshest value when it makes the read. 原因是两个线程可以在不同的处理器上运行,即使一个线程在另一个线程开始之前已经完成了很长时间,也不能保证第二个线程在进行读取时会获得最新的值。 If the field is not marked as volatile and no other synchronization is used, then the second thread could get a value that was cached locally on the processor it is running on. 如果该字段未标记为volatile并且未使用其他同步,则第二个线程可以获取在其运行的处理器上本地缓存的值。 That cached value could in theory be out-of-date for a long period of time, including after the first thread completed. 理论上缓存的值可能在很长一段时间内都是过时的,包括在第一个线程完成之后。

If you use volatile the value will always be written to and read from main memory, bypassing the processor's cached value. 如果使用volatile,则将始终将值写入主内存并从主内存中读取,从而绕过处理器的缓存值。

No, you may not need it. 不,你可能不需要它。 despite Mark Byers answer begin fairly accurate, it is limited. 尽管Mark Byers的回答开始相当准确,但它是有限的。 synchronized and volatile are not the only ways to correctly pass data between threads. synchronized和volatile不是在线程之间正确传递数据的唯一方法。 there are other, less talked about "synchronization points". 还有其他一些较少谈论“同步点”。 specifically, thread start and thread end are synchronization points. 具体而言,线程开始和线程结束同步点。 however, the thread which is starting Thread B must have recognized that Thread A is finished (eg by joining the thread or checking the thread's state). 但是,启动线程B的线程必须已经识别出线程A已完成(例如,通过连接线程或检查线程的状态)。 if this is the case, the the variable does not need to be volatile. 如果是这种情况,则变量不需要是volatile。

Possibly yes, unless you manually create a memory barrier. 可能是的,除非您手动创建内存屏障。 If A sets the variable, and B decides to take oit from some registry, you have a problem. 如果A设置变量,并且B决定从某个注册表中取出oit,那么就会出现问题。 So, you need a mmemory barrier, either implicit (lock, volatile) or explicit. 因此,您需要一个内存屏障,隐式(锁定,易失性)或显式。

http://java.sun.com/docs/books/jls/third_edition/html/memory.html#17.4.4 http://java.sun.com/docs/books/jls/third_edition/html/memory.html#17.4.4

The final action in a thread T1 synchronizes-with any action in another thread T2 that detects that T1 has terminated. 线程T1中的最终操作与另一个检测到T1已终止的线程T2中的任何操作同步。 T2 may accomplish this by calling T1.isAlive() or T1.join(). T2可以通过调用T1.isAlive()或T1.join()来完成此操作。

So it is possible to achieve your goal without using volatile. 因此,可以在不使用volatile的情况下实现目标。

In many cases, when there are apparent time dependencies, synchronization is being done by someone under the hood, and application doesn't need extra synchronization. 在许多情况下,当存在明显的时间依赖性时,同事正在由人员进行同步,并且应用程序不需要额外的同步。 Unfortunately this is not the rule, programmers must analyze each case carefully. 不幸的是,这不是规则,程序员必须仔细分析每个案例。

One example is Swing worker thread. 一个例子是Swing工作者线程。 People would do some calculation in a worker thread, save the result to a variable, then raise an event. 人们会在工作线程中进行一些计算,将结果保存到变量,然后引发事件。 The event thread will then read the result of the calculation from the variable. 然后,事件线程将从变量中读取计算结果。 No explicit synchronization is needed from application code, because "raising an event" already did synchronization, so writes from worker thread is visible from event thread. 应用程序代码不需要显式同步,因为“引发事件”已经进行了同步,因此工作线程的写入在事件线程中是可见的。

On one hand, this is a bliss. 一方面,这是一种幸福。 On the other hand, many people didn't understand this, they omit the synchronization simply because they never thought about the issue. 另一方面,许多人不理解这一点,他们省略了同步只是因为他们从未想过这个问题。 Their programs happen to be correct... this time. 他们的节目恰好是正确的......这一次。

If Thread A definitely dies before Thread B starts reading then it would be possible to avoid using volatile 如果线程A在线程B开始读取之前肯定死亡,则可以避免使用volatile

eg. 例如。

public class MyClass {

   volatile int x = 0;

   public static void main(String[] args) {

      final int i = x;
      new Thread() {
         int j = i;
         public void run() {
            j = 10;
            final int k = j;
            new Thread() {
               public void run() {
                  MyClass.x = k;
               }               
            }.start();
         }
      }.start();
   }
}

However, the problem is that whichever Thread starts Thread B will need to now that the value that Thread A is writing to has changed and to not use its own cached version. 但问题是,无论Thread启动哪个线程B,现在都需要线程A写入的值已经改变,并且不使用自己的缓存版本。 The easiest way to do this is to get Thread A to spawn Thread B. But if Thread A has nothing else to do when it spawns Thread B then this seems a little pointless (why not just use the same thread). 最简单的方法是让线程A生成线程B.但是如果线程A在生成线程B时没有别的事情可做,那么这似乎有点无意义(为什么不使用相同的线程)。

The other alternative is that if no other thread is dependent on this variable then maybe Thread A could initial a local variable with the volatile variable, do what it needs to do, and then finally write the contents of its local variable back to the volatile variable. 另一个替代方案是,如果没有其他线程依赖于此变量,则可能线程A可以使用volatile变量初始化局部变量,执行它需要执行的操作,然后最终将其局部变量的内容写回volatile变量。 Then when Thread B starts it initialises its local variable from the volatile variable and reads only from its local variable thereafter. 然后,当线程B启动时,它从volatile变量初始化其局部变量,然后仅从其局部变量读取。 This should massively reduce the amount of time spent keeping the volatile variable in sync. 这应该大大减少保持volatile变量同步所花费的时间。 If this solution seems unacceptable (because of other threads writing to the volatile variable or whatever) then you definitely need to declare the variable volatile. 如果这个解决方案似乎不可接受(因为其他线程写入volatile变量或其他),那么你肯定需要声明变量volatile。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 我需要使用易失性吗? - Do I need to use volatile? 如何确保写入发生在同一存储桶和同一键上的不同线程同时读取并发哈希映射之前? - How can I make sure that write happens before read in concurrenthashmap at the same time by different thread at same bucket and on a same key? 如何使连接线程保持活动状态? (我需要使用守护程序吗?) - How can I keep a connection thread alive? (Do I need to use a daemon?) 如何启动线程但保持一段时间? - How do I start a Thread but keep it alive for a certain time? 为什么我需要对volatile上的多个线程使用同步? - Why do I need to use synchronized for multiple threads over volatile? 线程不能同时在同一个套接字上读写流? - Thread cannot read and write streams on the same socket at the same time? 是否可以允许不同的线程同时读取和写入同一集合? - Is it possible to allow different thread both read to and write from the same collection? 我需要volatile关键字吗? (JAVA) - Do I need the volatile keyword? (Java) 当用户与代码的不同部分进行交互时,我是否需要一个线程来实时跟踪某些内容? - Do I need a thread to track something in real time while the user interacts with a different part of the code? 易失性读取是否发生在易失性写入之前? - Is volatile read happens-before volatile write?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM