标签: java-memory-model

Guava中使用的无锁延迟加载模式是否真的是线程安全的?

一些Guava内部类型,例如AbstractMultiset,有这样的模式:

private transient Set<E> elementSet;

@Override
public Set<E> elementSet() {
  Set<E> result = elementSet;
  if (result == null) {
    elementSet = result = createElementSet();
  }
  return result;
}

Set<E> createElementSet() {
  return new ElementSet();
}
Run Code Online (Sandbox Code Playgroud)

我们的想法是延迟创建集合视图(elementSet(),entrySet()),直到实际需要它们为止.进程周围没有锁定,因为如果两个线程同时调用elementSet(),则可以返回两个不同的值.编写该elementSet字段会有竞争,但由于写入引用字段在Java中始终是原子的,因此谁赢得竞争并不重要.

但是,我担心Java内存模型在这里内联的内容.如果createElementSet()ElementSet的构造函数都被内联,似乎我们可以得到这样的东西:

@Override
public Set<E> elementSet() {
  Set<E> result = elementSet;
  if (result == null) {
    elementSet = result = (allocate an ElementSet);
    (run ElementSet's constructor);
  }
  return result;
}
Run Code Online (Sandbox Code Playgroud)

这将允许另一个线程观察到一个非null但未完全初始化的值 …

java lazy-loading java-memory-model guava

15
推荐指数
1
解决办法
458
查看次数

Java final 字段:当前 JLS 是否可能出现“污点”行为

我目前正在尝试了解有关最终字段的 JLS 部分

为了更好地理解 JLS 中的文本,我还在阅读Jeremy Manson(JMM 的创建者之一)撰写的The Java Memory Model

该论文包含让我感兴趣的示例:如果o具有 final 字段的对象对另一个线程可见t两次:

  • o的构造函数完成之前首先“不正确地”
  • o的构造函数完成后的下一个“正确”

然后即使仅通过“正确”发布的路径访问它,也t可以看到半构造的o

这是论文中的部分:

图 7.3:简单最终语义示例

f1 是最后一个字段;它的默认值为 0

主题 1 主题 2 主题 3
o.f1 = 42;
p = o;
freeze o.f1;
q = o;

Run Code Online (Sandbox Code Playgroud)
r1 = p;
i = r1.f1;
r2 = q;
if (r2 == r1)
    k = r2.f1;
Run Code Online (Sandbox Code Playgroud)
r3 = q;
j = r3.f1;



Run Code Online (Sandbox Code Playgroud)

我们假设 r1、r2 和 …

java multithreading final java-memory-model jls

15
推荐指数
2
解决办法
684
查看次数

java:带有getter和setter的`volatile`私有字段

我们应该声明私有字段,就像volatile在多个线程中使用instanced一样吗?

Effective Java中,有一个例子,代码在没有volatile的情况下不起作用:

import java.util.concurrent.TimeUnit;

// Broken! - How long would you expect this program to run?
public class StopThread {
    private static boolean stopRequested; // works, if volatile is here

    public static void main(String[] args) throws InterruptedException {
        Thread backgroundThread = new Thread(new Runnable() {
            public void run() {
                int i = 0;
                while (!stopRequested)
                    i++;
            }
        });
        backgroundThread.start();
        TimeUnit.SECONDS.sleep(1);
        stopRequested = true;
    }
}
Run Code Online (Sandbox Code Playgroud)

解释说

while(!stopRequested)
    i++;
Run Code Online (Sandbox Code Playgroud)

优化到这样的事情:

if(!stopRequested)
    while(true)
        i++;
Run Code Online (Sandbox Code Playgroud)

所以stopRequested后台线程看不到进一步的修改,所以它永远循环.(顺便说一句,该代码在没有 …

java volatile memory-model java-memory-model

14
推荐指数
2
解决办法
2341
查看次数

初始化非最终字段

我目前正在阅读JSR-133(Java内存模型),我无法理解为什么fy可能未初始化(可能看到0).有人可以向我解释一下吗?

class FinalFieldExample {
    final int x;
    int y;
    static FinalFieldExample f;

    public FinalFieldExample() {
        x = 3;
        y = 4;
    }

    static void writer() {
        f = new FinalFieldExample();
    }

    static void reader() {
        if (f != null) {
            int i = f.x; // guaranteed to see 3
            int j = f.y; // could see 0
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

java multithreading java-memory-model

14
推荐指数
2
解决办法
699
查看次数

Java内存模型 - volatile和x86

我试图理解java volatile的内在函数及其语义,以及它对底层架构及其指令的转换.如果我们考虑以下博客和资源

为volatile生成的fences,在fence上读取/写入volatileStack overflow问题时会生成什么

这是我收集的内容:

  • volatile read在它之后插入loadStore/LoadLoad障碍(x86上的LFENCE指令)
  • 它可以防止随后的写入/加载重新排序负载
  • 它应该保证加载由其他线程修改的全局状态,即在LFENCE之后,由其他线程完成的状态修改对其CPU上的当前线程是可见的.

我正在努力理解的是:Java不会在x86上发出LFENCE,即读取volatile不会导致LFENCE ....我知道x86的内存排序会阻止lods/stored的负载重写,所以第二个要点是照顾.但是,我假设为了使该状态可以被该线程看到,应该发出LFENCE指令以保证在执行栅栏后的下一条指令之前所有LOAD缓冲区都被耗尽(根据英特尔手册).我知道x86上有cahce coherence协议,但是volatile读取仍然会消耗缓冲区中的任何LOAD,不是吗?

java cpu multithreading volatile java-memory-model

14
推荐指数
1
解决办法
317
查看次数

Scala和Java内存模型

Java内存模型(自1.5开始)将final字段与非final字段区别对待.特别是,如果this引用在构造期间没有转义,则构造函数中的final字段写入保证在其他线程上可见,即使该对象通过数据争用对另一个线程可用.(写入非final字段不保证是可见的,因此如果您不正确地发布它们,另一个线程可能会看到它们处于部分构造状态.)

是否有关于Scala编译器如何/如果为类创建final(而非非final)支持字段的文档?我查看了语言规范并搜索了网络,但找不到任何确定的答案.(作为比较的@scala.volatile注释记录到标记字段volatile)

concurrency scala final java-memory-model

13
推荐指数
1
解决办法
1593
查看次数

解释Java并发中的"程序顺序规则"

程序顺序规则指出"线程中的每个操作都发生在 - 在程序顺序中稍后出现的该线程中的每个操作之前"

1.I读取另一个线程,一个动作

  • 读取和写入变量
  • 锁定和解锁显示器
  • 开始和加入线程

这是否意味着可以按顺序更改读取和写入,但读取和写入不能使用在第2行或第3行中指定的操作更改顺序?

2."程序顺序"是什么意思?

对示例的解释将非常有用.

其他相关问题

假设我有以下代码:

long tick = System.nanoTime(); //Line1: Note the time
//Block1: some code whose time I wish to measure goes here
long tock = System.nanoTime(); //Line2: Note the time
Run Code Online (Sandbox Code Playgroud)

首先,它是一个单线程应用程序,以保持简单.编译器注意到它需要检查两次时间并且还注意到与周围时间注释行没有依赖关系的代码块,因此它看到了重新组织代码的可能性,这可能导致Block1不被定时调用所包围在实际执行期间(例如,考虑此顺序Line1-> Line2-> Block1).但是,作为程序员,我可以看到Line1,2和Block1之间的依赖关系.Line1应紧接在Block1之前,Block1需要有限的时间才能完成,并且Line2立即成功.

所以我的问题是:我是否正确测量了块?

  • 如果是,那么是什么阻止了编译器重新排列顺序.
  • 如果不是,(通过Enno的回答后认为是正确的)我该怎么做才能防止它.

PS:我最近在SO中提出的另一个问题偷了这个代码.

java concurrency java-memory-model

13
推荐指数
2
解决办法
3021
查看次数

Java - 对可变对象的易失性引用 - 对所有线程都可以看到对象字段的更新

...没有额外的同步?下面的Tree类应该由多个线程访问(它是单例但不通过枚举实现)

class Tree {

    private volatile Node root;

    Tree() {
        root = new Node();
        // the threads are spawned _after_ the tree is constructed
    }

    private final class Node {
        short numOfKeys;
    }
}
Run Code Online (Sandbox Code Playgroud)
  • numOfKeys读取器线程是否可以看到对该字段的更新而没有任何明确的同步(请注意,读者和编写者必须获取ReentrantReadWriteLock每个节点的相同实例 - 但禁止这样做)?如果不能使numOfKeys挥发性足够吗?
  • 将root更改为简单root = new Node()(除了调用Tree构造函数的主线程外,只有编写器线程会更改根)

有关:

编辑:对后5 Java语义感兴趣

java multithreading volatile java-memory-model

13
推荐指数
2
解决办法
7107
查看次数

为什么在CopyOnWriteArrayList中需要setArray()方法调用

在CopyOnWriteArrayList.java中,在方法集(int index,E element)下面

public E set(int index, E element) {
    final ReentrantLock lock = this.lock;
    lock.lock();
    try {
        Object[] elements = getArray();
        Object oldValue = elements[index];

        if (oldValue != element) {
            int len = elements.length;
            Object[] newElements = Arrays.copyOf(elements, len);
            newElements[index] = element;
            setArray(newElements);
        } else {
            // Not quite a no-op; ensures volatile write semantics
            setArray(elements);----? Why this call required?
        }
        return (E)oldValue;
    } finally {
        lock.unlock();
    }
}
Run Code Online (Sandbox Code Playgroud)

为什么需要调用setArray()?我无法理解上面那个方法调用的注释.是因为我们没有使用synchronized块,我们必须手动刷新我们使用的所有变量吗?在上面的方法中,他们使用了重入锁.如果他们使用了synchronized语句,他们仍然需要调用setArray()方法吗?我想不是.

问题2:如果我们最终在else中,这意味着我们没有修改元素数组,那么为什么我们需要刷新变量数组的值?

java collections java-memory-model locks

13
推荐指数
2
解决办法
868
查看次数

Java内存模型:创建最终实例字段的循环参考图是否安全,所有这些都在同一个线程中分配?

能比我更了解Java内存模型的人确认我理解以下代码是否正确同步?

class Foo {
    private final Bar bar;

    Foo() {
        this.bar = new Bar(this);
    }
}

class Bar {
    private final Foo foo;

    Bar(Foo foo) {
        this.foo = foo;
    }
}
Run Code Online (Sandbox Code Playgroud)

我知道这段代码是正确的,但我没有完成整个过程- 在数学之前.我确实找到了两个非正式的引文,表明这是合法的,尽管我有点担心完全依赖它们:

最终字段的使用模型很简单:在该对象的构造函数中设置对象的最终字段; 并且在对象的构造函数完成之前,不要在另一个线程可以看到的地方写入对正在构造的对象的引用.如果遵循此原因,那么当另一个线程看到该对象时,该线程将始终看到该对象的最终字段的正确构造版本.它还将看到那些最终字段引用的任何对象或数组的版本,这些字段至少与最终字段一样是最新的.[ Java®语言规范:Java SE 7 Edition,第17.5节 ]

另一个参考:

对象的正确构造意味着什么?它只是意味着在构造过程中不允许对正在构造的对象的引用"逃逸".(有关示例,请参阅安全构造技术.)换句话说,不要在另一个线程可能看到它的任何地方放置对正在构造的对象的引用; 不要将它分配给静态字段,不要将其注册为任何其他对象的侦听器,依此类推.这些任务应在构造函数完成后完成,而不是在构造函数中完成.[ JSR 133(Java内存模型)常见问题解答,"最终字段如何在新JMM下工作?" ]

java concurrency multithreading memory-model java-memory-model

13
推荐指数
2
解决办法
290
查看次数