一些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但未完全初始化的值 …
我目前正在尝试了解有关最终字段的 JLS 部分。
为了更好地理解 JLS 中的文本,我还在阅读Jeremy Manson(JMM 的创建者之一)撰写的The Java Memory Model。
该论文包含让我感兴趣的示例:如果o具有 final 字段的对象对另一个线程可见t两次:
o的构造函数完成之前首先“不正确地”o的构造函数完成后的下一个“正确”然后即使仅通过“正确”发布的路径访问它,也t可以看到半构造的o。
这是论文中的部分:
图 7.3:简单最终语义示例
f1 是最后一个字段;它的默认值为 0
主题 1 主题 2 主题 3 Run Code Online (Sandbox Code Playgroud)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;我们假设 r1、r2 和 …
我们应该声明私有字段,就像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后台线程看不到进一步的修改,所以它永远循环.(顺便说一句,该代码在没有 …
我目前正在阅读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 volatile的内在函数及其语义,以及它对底层架构及其指令的转换.如果我们考虑以下博客和资源
为volatile生成的fences,在fence上读取/写入volatile和Stack overflow问题时会生成什么
这是我收集的内容:
我正在努力理解的是:Java不会在x86上发出LFENCE,即读取volatile不会导致LFENCE ....我知道x86的内存排序会阻止lods/stored的负载重写,所以第二个要点是照顾.但是,我假设为了使该状态可以被该线程看到,应该发出LFENCE指令以保证在执行栅栏后的下一条指令之前所有LOAD缓冲区都被耗尽(根据英特尔手册).我知道x86上有cahce coherence协议,但是volatile读取仍然会消耗缓冲区中的任何LOAD,不是吗?
Java内存模型(自1.5开始)将final字段与非final字段区别对待.特别是,如果this引用在构造期间没有转义,则构造函数中的final字段写入保证在其他线程上可见,即使该对象通过数据争用对另一个线程可用.(写入非final字段不保证是可见的,因此如果您不正确地发布它们,另一个线程可能会看到它们处于部分构造状态.)
是否有关于Scala编译器如何/如果为类创建final(而非非final)支持字段的文档?我查看了语言规范并搜索了网络,但找不到任何确定的答案.(作为比较的@scala.volatile注释被记录到标记字段volatile)
程序顺序规则指出"线程中的每个操作都发生在 - 在程序顺序中稍后出现的该线程中的每个操作之前"
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立即成功.
所以我的问题是:我是否正确测量了块?
PS:我最近在SO中提出的另一个问题偷了这个代码.
...没有额外的同步?下面的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 = new Node()(除了调用Tree构造函数的主线程外,只有编写器线程会更改根)有关:
编辑:对后5 Java语义感兴趣
在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内存模型的人确认我理解以下代码是否正确同步?
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
java ×9
concurrency ×3
volatile ×3
final ×2
memory-model ×2
collections ×1
cpu ×1
guava ×1
jls ×1
lazy-loading ×1
locks ×1
scala ×1