Java使用synchronized时是否免费获得易失性功能?

Java使用synchronized时是否免费获得易失性功能?,java,multithreading,concurrency,synchronized,volatile,Java,Multithreading,Concurrency,Synchronized,Volatile,我读了好几篇关于并发问题的帖子,但我仍然不确定。 我可以说,当使用synchronized时,我可以免费获得volatile功能,因为当对象上的锁被释放时,下一个线程总是读取修改过的对象。使用volatile,对象的值会立即反映到其他线程。但是当我使用synchronized时,由于对象上的锁,不可能立即反映它。当锁被释放时,只有另一个线程才能访问它。因此,我不必关心将值立即反映给其他线程。我理解得对吗 [更新] 示例打印始终为1 2 3 4 5 6 7 8 9,无挥发性 package mai

我读了好几篇关于并发问题的帖子,但我仍然不确定。 我可以说,当使用synchronized时,我可以免费获得volatile功能,因为当对象上的锁被释放时,下一个线程总是读取修改过的对象。使用volatile,对象的值会立即反映到其他线程。但是当我使用synchronized时,由于对象上的锁,不可能立即反映它。当锁被释放时,只有另一个线程才能访问它。因此,我不必关心将值立即反映给其他线程。我理解得对吗

[更新]
示例打印始终为1 2 3 4 5 6 7 8 9,无挥发性

package main;

public class Counter
{
    public static long count = 0;
}

public class UseCounter implements Runnable
{
    public void increment()
    {
        synchronized (this)
        {       
            Counter.count++;
            System.out.print(Counter.count + " ");
        }
    }

    @Override
    public void run()
    {
        increment();
        increment();
        increment();
    }
}

public class DataRace
{
    public static void main(String args[])
    {
        UseCounter c = new UseCounter();

        Thread t1 = new Thread(c);
        Thread t2 = new Thread(c);
        Thread t3 = new Thread(c);

        t1.start();
        t2.start();
        t3.start();
    }
}

拥有易失性变量与同步访问不同。当您将变量标记为volatile时,
线程
访问该对象的对象将不会保留本地缓存,并且只有一个“副本”。如果您将这两个(同步和易失性)结合起来,那么它将始终是更新版本,并且您对它的访问不会有冲突。

否,根据Java内存模型,同步访问并不意味着易失性访问(尽管在特定实现中可能是,但您不应该依赖于此)

同步操作会导致与上的关系同步 行动,定义如下:

  • 监视器m上的解锁操作与所有后续锁定同步 m上的操作(其中“后续”是根据 同步顺序)

  • 对易失性变量v(§8.3.1.4)的写入与所有 任何线程对v的后续读取(其中定义了“后续 根据同步顺序)

volatile
对变量进行操作,
synchronized
对对象的监视器(“锁”)进行操作


如果一个线程A刚刚退出对象O上的同步块,而另一个线程B刚刚读取对象O上的易失性变量(实例字段)V,则两个线程之间仍有同步的关系。不能保证线程A会看到线程B所做的任何数据修改,反之亦然,除非线程B也在对象O上同步,或者线程A也在对象O上访问了volatile字段V

您的代码保证打印出123456789。原因是如果您有一个序列,例如

  • 线程t1写入Counter.count
  • 线程t1解锁对象c
  • 线程t2锁定对象c
  • 线程t2读取Counter.count
  • 然后保证步骤4中的读取在步骤1中看到写入


    这与
    volatile
    不同,因为不能保证写入会立即反映回内存,而只能保证在步骤3结束时t2可以看到步骤1中的写入。

    谢谢,但我完全不理解为什么存在冲突访问,因为当我使用synchronized锁定和释放对象时,我是否需要关心第一个线程是否制作了它的本地副本?当对象被解锁时,难道不能保证下一个线程可以访问修改过的对象吗?
    volatile
    背后的思想是,您将始终获得变量的最新版本。当一个线程保持缓存时,它会这样做以节省获取新值的时间,但volatile变量不会是cachedOk,这是否意味着我在多个线程访问外部变量时也应该使用volatile?看看我最新的问题。它总是打印1 2 3 4 5 6 7 8 9,没有挥发性。这意味着它不能保证这样做?当一个线程B在另一个线程a读取(或写入)一个易失性字段之后读取(或写入)该字段,那么线程B将看到线程a对任何变量所做的所有修改(在它访问同一个易失性字段之前),包括非易失性修改。访问相同的可变变量会在执行此操作的线程之间创建一个“之前发生”关系。关于您的更新:它当前打印的内容几乎没有任何意义。数据竞争条件通常在您最不期望的时候出现,并且很难重现和解决。重要的是您是否遵循了Java内存模型的规则。好的,谢谢。虽然我不能在没有volatile的情况下重现错误行为(即使在循环中运行代码1.000.000次时也不行),但我会记住这一点。