Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/337.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 我需要将标志放入同步块中吗?_Java_Multithreading_Concurrency - Fatal编程技术网

Java 我需要将标志放入同步块中吗?

Java 我需要将标志放入同步块中吗?,java,multithreading,concurrency,Java,Multithreading,Concurrency,我有这样一个代码: if(!flag) { synchronized(lock) { lock.wait(1000); } } if(!flag) { print("Error flag not set!"); } 以及: 我的一个朋友告诉我应该在synchronized块中放入flag=true: synchronized(lock) { flag = true; lock.notify() } 我不明白为什么。这是典型的例子吗?有人

我有这样一个代码:

if(!flag) {
   synchronized(lock) {
     lock.wait(1000);
   }
}
if(!flag) { print("Error flag not set!"); }
以及:

我的一个朋友告诉我应该在synchronized块中放入flag=true:

  synchronized(lock) {
      flag = true;
      lock.notify()
   }
我不明白为什么。这是典型的例子吗?有人能解释一下吗


如果我声明我的标志为volatile,那么我就不需要将其放入同步块中?

由于多个线程使用了
标志
变量,因此必须使用某种机制来确保更改的可见性。这确实是多线程中常见的模式。Java内存模型不保证其他线程会看到
标志的新值

这是为了实现现代多处理器系统所采用的优化,在这些系统中,始终保持缓存一致性的成本可能非常高。内存访问通常比其他“普通”CPU操作慢几个数量级,因此现代处理器会尽可能地避免内存访问。相反,频繁访问的位置保存在小型、快速的本地处理器内存(缓存)中。仅对缓存进行更改,并在某些点刷新到主内存。这对于一个处理器来说很好,因为内存内容不会被其他方更改,所以我们保证缓存内容反映内存内容。(好吧,这太简单了,但从高级编程的角度来看,我认为这是不相关的)。问题是,一旦我们添加另一个处理器,独立地更改内存内容,这种保证就丢失了。为了缓解这个问题,设计了各种缓存一致性协议(有时是精心设计的,请参见例如)。不过,毫不奇怪,它们需要一些簿记和处理器间通信开销

另一个稍微相关的问题是写操作的原子性。基本上,即使其他线程看到了更改,也可以看到部分更改。在java中,这通常不是什么问题,因为语言规范保证了所有写操作的原子性。但是,对64位原语(
long
double
)的写入被明确地称为两个独立的32位写入:

对于Java编程语言内存模型而言,对非易失性长或双值的单次写入被视为两次单独的写入:每32位半写一次。这可能导致线程在一次写入中看到64位值的前32位,在另一次写入中看到第二个32位。()


回到有问题的代码。。。需要同步,并且
synchronized
块满足需要。尽管如此,我还是发现让这样的标志变得易变更令人愉快。Net效果是相同的-可见性保证和原子写入-但它不会用小的
同步的
块使代码混乱。

如果您正在检查和修改来自不同线程的标志,则需要至少声明它
volatile
,以便线程看到更改

将检查放入同步块也会起作用


是的,它是并发中一个非常基本的东西,所以你应该确保你阅读了内存模型,
发生在
和其他相关主题之前。

主内存很慢。真慢。现在,CPU中的内部缓存速度快了1000倍左右。出于这个原因,现代代码试图在CPU的缓存中保存尽可能多的数据

主内存如此之慢的原因之一是它是共享的。当您更新主内存时,所有CPU内核都会收到更改通知。另一方面,缓存是每个核心的。这意味着当线程A更新标志时,它只更新自己的缓存。其他线程可能会也可能不会看到更改

有两种方法可确保将标志写入主存:

  • 将其放入
    同步的
    块中
  • 声明它是易变的
  • volatile
    的优点是,对标志的任何访问都将确保主内存中标志的状态得到更新。当您在许多地方使用该标志时,请使用此选项


    在您的例子中,您已经有了
    同步的
    块。但是在第一种情况下,第一个
    if
    可能正在读取一个过时的值(即线程可能
    wait()
    ,即使该标志已经是
    true
    )。因此,您仍然需要volatile,首先:lock.wait(1000)将在一秒钟后返回,即使另一个线程没有发送notify

    第二:您的朋友是对的,在这种情况下,您共享了由不同线程访问的数据,因此访问数据时最好使用类似于代码中的锁进行保护

    第三:将您的标志变量标记为volatile,以便不同的线程确保它们始终使用最后一个“writed”值


    最后:我还将if(!flag)代码放入同步块->它也访问标志变量…

    不仅如此,而且第一个代码段在检查标志时应该始终保持锁。添加到上面的注释中,
    wait
    应该在
    中,而
    loop我建议您阅读,看看应该与
    wait
    @assylas一起使用的规范模式,因为您提到的javadoc没有回答这个问题。不管怎样,我知道了,在这种情况下,挥发性就足够了。所以当我的朋友走的时候,这条线停了,那条线开始了,他在胡说八道,他从来没有提到过volatile@Alex标记标志是不够的。您仍然需要使用while循环:
    while(!flag){synchronized(lock){lock.wait();}}
    (因为
    wait
    即使不调用
    notify
    ),也可能会被唤醒)-并且因为您确实需要输入一个同步块,您还可以删除volatile关键字并在synchronized块中包含
    while
    synchronized(lock){while(!flag)lock.wait();}
    ,这正是javadoc中建议的代码
      synchronized(lock) {
          flag = true;
          lock.notify()
       }