标签: java-memory-model

有效不可变对象

我想确保根据Java内存模型正确理解'有效不可变对象'的行为.

假设我们有一个可变类,我们希望将其发布为有效的不可变类:

class Outworld {
  // This MAY be accessed by multiple threads
  public static volatile MutableLong published;
}

// This class is mutable
class MutableLong {
  private long value;

  public MutableLong(long value) {
    this.value = value;
  }

  public void increment() {
    value++;
  }

  public long get() {
    return value;
  }
}
Run Code Online (Sandbox Code Playgroud)

我们执行以下操作:

// Create a mutable object and modify it
MutableLong val = new MutableLong(1);
val.increment();
val.increment();
// No more modifications
// UPDATED: Let's say for this example we …
Run Code Online (Sandbox Code Playgroud)

java concurrency immutability java-memory-model

10
推荐指数
2
解决办法
2015
查看次数

我是否有重新排序问题,是否因为参考逃脱?

我有这个类,我在那里使用它们来缓存实例并克隆它们(数据是可变的).

我想知道我是否可以面对这个重新排序的问题.

我已经看过这个答案和JLS,但我仍然没有信心.

public class DataWrapper {
    private static final ConcurrentMap<String, DataWrapper> map = new ConcurrentHashMap<>();
    private Data data;
    private String name;

    public static DataWrapper getInstance(String name) {
        DataWrapper instance = map.get(name);
        if (instance == null) {
            instance = new DataWrapper(name);
        }
        return instance.cloneInstance();
    }

    private DataWrapper(String name) {
        this.name = name;
        this.data = loadData(name);  // A heavy method
        map.put(name, this);  // I know
    }

    private DataWrapper cloneInstance() {
        return new DataWrapper(this);
    }

    private DataWrapper(DataWrapper that) {
        this.name …
Run Code Online (Sandbox Code Playgroud)

java multithreading java-memory-model happens-before

10
推荐指数
2
解决办法
317
查看次数

Java内存模型同步:如何诱导数据可见性bug?

"Java Concurrency in Practice"给出了以下不安全类的示例,由于Java内存模型的性质可能最终会永久运行或打印0.

这个类试图证明的问题是这里的变量不是线程之间的"共享".因此,线程看到的值可能与另一个线程不同,因为它们不是易失性或同步的.另外由于JVM ready = true允许的语句的重新排序可能在number = 42之前设置.

对我来说,这个类总是使用JVM 1.6正常工作.有关如何让这个类执行不正确的行为(即打印0或永远运行)的任何想法?

public class NoVisibility {
    private static boolean ready;
    private static int number;

    private static class ReaderThread extends Thread {
        public void run() {
            while (!ready)
                Thread.yield();
            System.out.println(number);
        }
    }

    public static void main(String[] args) {
        new ReaderThread().start();
        number = 42;
        ready = true;
    }
}
Run Code Online (Sandbox Code Playgroud)

java concurrency synchronization java-memory-model

9
推荐指数
2
解决办法
849
查看次数

使用volatile和synchronized时,刷新或发布到各种线程的内存范围是多少?

这个问题仅涉及内存可见性,不会发生在之前和发生之后.Java中有四种方法可以保证一个线程中的内存更改对另一个线程可见.(参考http://gee.cs.oswego.edu/dl/cpj/jmm.html)

  1. 写入线程释放同步锁,读取线程随后获取相同的同步锁.
  2. 如果一个字段被声明为volatile,那么在写入器线程执行任何进一步的内存操作之前,写入它的任何值都会被写入线程刷新并使其可见(即,为了手头的目的,它立即被刷新).
  3. 线程第一次访问对象的字段时,它会看到字段的初始值或自某个其他线程写入的值.
  4. 当一个线程终止时,所有写入的变量都被刷新到主存储器.

根据Java Concurrency in Practice,关于这些问题的圣经:

volatile变量的可见性效果超出了volatile变量本身的值.当线程写入到易失性可变,并随后线程读取相同的变量,的值的所有变量都是对可见光于写入所述易失性可变变得可见之前B看书挥发性变量之后.

挥发性的问题

这是否意味着JVM实际上跟踪了易失性变量读写,以便知道如何将内存从A刷新到B而不是AC?所以A写入变量,后来C从变量读取,然后B从变量中读取,刷新是在AB以及AC之间以每线程完成的,而不是 BC?或者,它是否暗示所有缓存的内存都被刷新,无论线程如何?是只刷新的volatile变量,还是所有缓存的内存?

同步问题

对于synchronized关键字刷新,它表示只有锁内部更新的内存才能保证发布到其他线程.这意味着在下面的代码中,两个线程在运行method(),同步块将刷新staticVar2到另一个线程,但不是 staticVar1,这是正确的吗?

此外,如果另一个线程正在执行method2(),同步differentLock可以导致发生 - 在发生之前 - 发生问题method().但是,问题在于可见性.如果线程A执行method,那么后面的线程B执行method2() …

java synchronization memory-model java-memory-model

9
推荐指数
1
解决办法
1495
查看次数

64位OpenJDK 7/8中并发长写的值完整性保证

注意:此问题与volatile,AtomicLong或所描述的用例中的任何感知缺陷无关.

我试图证明或排除的财产如下:

鉴于以下内容:

  • 最近的64位OpenJDK 7/8(最好7位,但8位也很有帮助)
  • 多处理英特尔基础系统
  • 非易失性长原始变量
  • 多个不同步的mutator线程
  • 一个不同步的观察者线程

观察者是否始终保证会遇到由变异线程写的完整值,或者是撕裂危险的单词?

JLS:不确定

此属性对于32位基元和64位对象引用是存在的,但是对于long和double,JLS不保证:

17.7.非原子对double和long的处理:
出于Java编程语言内存模型的目的,对非易失性long或double值的单次写入被视为两个单独的写入:每个32位一半写入一次.这可能导致线程从一次写入看到64位值的前32位,而从另一次写入看到第二次32位的情况.

但是抱着你的马:

[...]为了效率,这种行为是特定于实现的; Java虚拟机的实现可以自由地以原子方式或分两部分执行对long和double值的写入.鼓励Java虚拟机的实现避免在可能的情况下拆分64位值.[...]

因此,JLS 允许 JVM实现拆分64位写入,并鼓励开发人员相应地进行调整,但也鼓励 JVM实现者坚持使用64位写入.我们还没有回答最新版本的HotSpot.

HotSpot JIT:谨慎乐观

由于单词撕裂最有可能发生在紧密循环和其他热点的范围内,我试图分析JIT编译的实际汇编输出.长话短说:需要进一步测试,但我只能在long上看到原子64位操作.

我使用了hdis,一个OpenJDK的反汇编插件.在我老化的OpenJDK 7u25版本中构建并安装了插件之后,我开始编写一个简短的程序:

public class Counter {
  static long counter = 0;
  public static void main(String[] _) {
    for (long i = (long)1e12; i < (long)1e12 + 1e5; i++)
      put(i);
    System.out.println(counter);
  }

  static void put(long v) {
    counter += v;
  }
}
Run Code Online (Sandbox Code Playgroud)

我确保始终使用大于MAX_INT(1e12到1e12 + 1e5)的值,并重复操作足够的次数(1e5)以触发JIT.

编译后,我用hdis执行Counter.main(),如下所示:

java -XX:+UnlockDiagnosticVMOptions \ 
     -XX:PrintAssemblyOptions=intel \
     -XX:CompileCommand=print,Counter.put …
Run Code Online (Sandbox Code Playgroud)

java concurrency jvm jvm-hotspot java-memory-model

9
推荐指数
2
解决办法
628
查看次数

理解发生在之前和同步

我正在尝试理解Java 发生 - 在订单概念之前,有一些东西看起来很混乱.据我所知,之前发生的只是行动集上的一个订单,并没有提供有关实时执行订单的任何保证.实际上(强调我的):

应该注意的是,两个动作之间存在的先发生关系并不一定意味着它们必须在实现中以该顺序发生.如果重新排序产生的 结果与合法执行一致,则不是非法的.

因此,所有它说的是,如果有两个动作w(写)和r(读),使得HB(W,R) ,较r 可能实际发生之前,w在执行,但不能保证它会的.w读取也会观察到写入r.

我如何确定在运行时随后执行两个操作?例如:

public volatile int v;
public int c;
Run Code Online (Sandbox Code Playgroud)

操作:

Thread A
v = 3;  //w

Thread B
c = v;  //r
Run Code Online (Sandbox Code Playgroud)

这里我们有,hb(w, r)但这并不意味着在分配后c会包含价值3.如何强制执行c3?同步订单是否提供此类保证?

java multithreading java-memory-model

9
推荐指数
1
解决办法
2111
查看次数

对象构造是否在实践中保证所有线程都看到非最终字段已初始化?

Java的内存模型保证了之前发生的对象的构造和释放的关系:

从对象的构造函数的末尾到该对象的终结器(第12.6节)的开始有一个发生前的边缘.

以及最终字段的构造函数和初始化:

当构造函数完成时,对象被认为是完全初始化的.在该对象完全初始化之后只能看到对象引用的线程可以保证看到该对象的最终字段的正确初始化值.

还有一个关于volatile字段的保证,因为关于对这些字段的所有访问的关系都存在:

写入易失性字段(第8.3.1.4节) - 在每次后续读取该字段之前发生.

但是常规的,古老的非易失性领域呢?我已经看到很多多线程代码在使用非易失性字段构造对象后不会创建任何类型的内存屏障.但是我从来没有见过或听说过任何问题,而且我自己也无法重建这种局部结构.

现代JVM在施工后是否只是放置了内存屏障?避免在施工周围重新排序?还是我很幸运?如果是后者,是否可以编写可以随意重现部分构造的代码?

编辑:

澄清一下,我说的是以下情况.假设我们有一个班级:

public class Foo{
    public int bar = 0;

    public Foo(){
        this.bar = 5;
    }
    ...
}
Run Code Online (Sandbox Code Playgroud)

一些Thread T1实例化一个新Foo实例:

Foo myFoo = new Foo();

然后将实例传递给其他线程,我们将调用它T2:

Thread t = new Thread(() -> {
     if (myFoo.bar == 5){
         ....
     }
});
t.start();
Run Code Online (Sandbox Code Playgroud)

T1 进行了两次我们感兴趣的写作:

  1. T1写bar了新实例化的值5myFoo
  2. T1将对新创建的对象的引用写入myFoo变量

对于T1,我们得到写#1 发生保证- 在写#2 之前:

线程中的每个动作都发生 …

java multithreading jvm java-memory-model

9
推荐指数
1
解决办法
238
查看次数

Java Memory Model: a JLS statement about sequential consistency seems incorrect

I'm reading Chapter 17. Threads and Locks of JLS and the following statement about sequential consistency in Java seems incorrect to me:

If a program has no data races, then all executions of the program will appear to be sequentially consistent.

They define a data race as:

When a program contains two conflicting accesses (§17.4.1) that are not ordered by a happens-before relationship, it is said to contain a data race.

They define conflicted accesses as:

Two accesses to (reads …

java java-memory-model jls

8
推荐指数
1
解决办法
202
查看次数

线程是否可以先通过安全发布获取对象,然后不安全地发布它?

阅读此答案后,我想到了这个问题

代码示例:

class Obj1 {
  int f1 = 0;
}

volatile Obj1 v1;
Obj1 v2;

Thread 1            | Thread 2 | Thread 3
-------------------------------------------------
var o = new Obj1(); |          |
o.f1 = 1;           |          |
v1 = o;             |          |
                    | v2 = v1; |
                    |          | var r1 = v2.f1;

Is (r1 == 0) possible?
Run Code Online (Sandbox Code Playgroud)

这里的对象o

  • 首次安全发布:从Thread 1Thread 2通过volatile字段v1
  • 然后不安全地发布:从Thread 2Thread 3 …

java multithreading java-memory-model jls safe-publication

8
推荐指数
2
解决办法
373
查看次数

最终的多线程保证和Java中的内存模型有什么关系?

内存模型在 17.4 中定义。内存模型

17.5 中给出了现场final多线程保证。最终字段语义

我不明白为什么这些是单独的部分。

AFAIKfinal和内存模型都提供了一些保证。
任何真正的程序执行都必须遵守这两个保证。
但现在很清楚这些final保证是否适用于用于验证 17.4.8 中因果关系要求的中间执行。执行和因果关系要求

另一个不清楚的时刻是17.5.1。Final Fields 的语义定义了一个新的“special” ,它与内存模型happens-before中的不同:happens-before

happens-before排序不会与其他happens-before排序传递地关闭。

如果它们相同happens-before,则happens-before不再是偏序(因为它不具有传递性)。
我不明白这怎么不会破坏事情。

如果这些不同happens-before,那么就不清楚 17.5 中的是什么。Final Field Semantics确实如此。17.4中
内存模型用于限制读取可以返回的内容:happens-before

非正式地,如果没有happens-before排序来阻止r读取,则允许读取查看写入的结果。w

但是17.5。最后的字段语义是一个不同的部分。

java final java-memory-model jls happens-before

8
推荐指数
1
解决办法
181
查看次数