如何确保使用volatile变量在关闭操作之后永远不会执行写操作?

时间:2019-07-16 08:10:22

标签: java multithreading

考虑以下代码:

Thread A:
closed = true; // closed is a volatile variable
close();

Thread B:
while(true){
  if(!closed){
    write();
  }
  else {
    ...
  }
}

如果我理解正确,closed = true;发生在close();之前,而closed = true;发生在write();之前,但是{ {1}}和close();

以上代码是否确保write();仅在write();之前被调用?如果没有,可以进行哪些修改以使其起作用?还是我必须使用close()

3 个答案:

答案 0 :(得分:1)

您已经提供了答案:“还是我必须使用同步?”是的!

答案 1 :(得分:1)

基本上,这里需要的是一个顺序,即write之前执行的close()语句。

  1. Volatile将无济于事,因为它只是将变量直接写入内存。 close()语句没有任何内容
  2. Synchronized关键字只是阻止另一个线程进入在同一监视器上同步的另一个/相同的同步块。如果两个任务都必须由不同的线程来完成,那么再也不太适合订购。

在Kotlin中解决该问题的非常简单的示例(java将是类似的)。您创建一个可用于锁定的对象。

  1. 您的关闭线程A将等待获取对对象lock的锁定
  2. 另一个线程B不会等待获取锁。相反,它将关闭并通知所有线程执行其工作。
  3. A现在可以进入关键部分close()并再次释放锁。

val lock = Object()

val A = Thread{        
   synchronized(lock) {
       lock.wait()
       closed = true; // closed is a volatile variable
       close();
       lock.notifyAll()
   }
}

val B = Thread{
   synchronized(lock) {
       write()
       lock.notifyAll()
   }
}

t.start()
t1.start()

t.join()
t1.join()

请注意,此方法存在很大缺陷,是使您步入正轨的基本示例。您可能要使用可重入锁,并仔细阅读完整代码。

答案 2 :(得分:1)

您不能保证// webpack config plugins: [ new CopyWebpackPlugin([ { from: resolvePath('static'), to: resolvePath(isCordova ? 'cordova/www/static' : 'www/static'), }, ]), new CleanWebpackPlugin() ] 发生在 close()之前。

您有write()作为各种信号量。您可以使用它来表示事件,但不要使用它来保证相互排斥。在您的情况下,线程A 可以向线程B 发出信号(A正在写入,B从volatile closed变量中读取),但是线程A 无法收到来自线程B 的信号,该信号导致出现如下所述的问题情况

volatile仅保证读取的内容可以看到最新的写入内容,但不保证您的写入内容发生在读取之前

有问题的解决方案

线程B 可能位于volatile的中间,而线程A 翻转write()并继续执行closed = true。 / p>

通过保护close()的整个执行来使用显式锁定,并使用相同的锁定来确保在编写过程中不调用write()。 您可以通过根据Mangat的答案同步同一对象或使用SDK中的 ReadWriteLock 来做到这一点。