我的老师在一个关于线程的上层Java课上说了一些我不确定的东西.
他表示以下代码不一定会更新ready变量.根据他的说法,两个线程不一定共享静态变量,特别是在每个线程(主线程对ReaderThread)在其自己的处理器上运行并因此不共享相同的寄存器/缓存/等和一个CPU的情况下不会更新另一个.
从本质上讲,他说有可能ready在主线程中更新,但不在主线程中更新ReaderThread,因此ReaderThread将无限循环.
他还声称该程序可以打印0或42.我知道如何42打印,但不是0.他提到当number变量设置为默认值时会出现这种情况.
我想也许并不能保证在线程之间更新静态变量,但这对我来说非常奇怪.制作readyvolatile会纠正这个问题吗?
他展示了这段代码:
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) 我想澄清一下,在关系与volatile变量一起工作之前会发生什么.我们有以下变量:
public static int i, iDst, vDst;
public static volatile int v;
Run Code Online (Sandbox Code Playgroud)
和线程A:
i = 1;
v = 2;
Run Code Online (Sandbox Code Playgroud)
和线程B:
vDst = v;
iDst = i;
Run Code Online (Sandbox Code Playgroud)
以下语句是否符合Java内存模型(JMM)?如果没有,那么正确的解释是什么?
i = 1总是发生在以前 v = 2v = 2 发生 vDst = v在JMM 之前,只有它实际发生在时间之前i = 1 发生 iDst = i在JMM 之前(并且iDst可以预测分配1)如果v = 2实际发生vDst = v在时间之前i = 1和之间的顺序iDst …java volatile java-memory-model thread-synchronization happens-before
以下代码示例显示了一种常见方法,用于演示由于缺少发生之前关系而导致的并发问题。
private static /*volatile*/ boolean running = true;
public static void main(String[] args) throws InterruptedException {
new Thread() {
@Override
public void run() {
while (running) {
// Do nothing
}
}
}.start();
Thread.sleep(1000);
running = false;
}
Run Code Online (Sandbox Code Playgroud)
如果running是volatile,程序保证在大约一秒后终止。但是,如果running不是volatile,则根本无法保证程序终止(因为在running这种情况下没有发生之前关系或保证变量更改的可见性),而这正是我的测试中发生的情况。
根据JLS 17.4.5,人们还可以通过写入和读取另一个volatile变量来强制执行发生前关系running2,如以下代码示例所示。
private static boolean running = true;
private static volatile boolean running2 = true;
public static void main(String[] args) throws InterruptedException {
new Thread() …Run Code Online (Sandbox Code Playgroud) java multithreading volatile java-memory-model happens-before
以下是经典Concurency in Practice:
当线程A写入volatile变量并且随后线程B读取相同的变量时,在写入volatile变量之前A可见的所有变量的值在读取volatile变量后变为B可见.
我不确定我是否真的能理解这句话.例如,在这种情况下所有变量的含义是什么?这是否意味着使用volatile也会对非易失性变量的使用产生副作用?
在我看来,这句话有一些我无法理解的微妙含义.
有帮助吗?
假设我有两个运行方式:
线程A快速执行工作,比如说每秒100万次更新,所以我怀疑经常在锁/互斥锁/监视器上锁定和解锁是个坏主意.但是如果没有锁定并且无法建立从线程A到线程B的先发生关系,那么通过Java内存模型(JMM规范),线程B根本不能保证看到A对图像的任何更新.
所以我认为最小的解决方案是线程A和B在同一个共享锁上定期同步,但在synchronized块内部实际上不执行任何工作 - 这就是使模式非标准和可疑的原因.用半实半伪代码来说明:
class ComputationCanvas extends java.awt.Canvas {
private Object lock = new Object();
private int[] pixels = new int[1000000];
public ComputationCanvas() {
new Thread(this::runThreadA).start();
new Thread(this::runThreadB).start();
}
private void runThreadA() {
while (true) {
for (1000 steps) {
update pixels directly
without synchornization
}
synchronized(lock) {} // Blank
}
}
private void runThreadB() {
while (true) {
Thread.sleep(100);
synchronized(lock) {} // Blank
this.repaint();
}
}
@Override
public void paint(Graphics g) {
g.drawImage(pixels, 0, 0);
} …Run Code Online (Sandbox Code Playgroud) java multithreading synchronization java-memory-model happens-before
番石榴供应商级包含MemoizingSupplier:
static class MemoizingSupplier<T> implements Supplier<T>, Serializable {
final Supplier<T> delegate;
transient volatile boolean initialized;
// "value" does not need to be volatile; visibility piggy-backs
// on volatile read of "initialized".
transient T value;
MemoizingSupplier(Supplier<T> delegate) {
this.delegate = delegate;
}
@Override public T get() {
// A 2-field variant of Double Checked Locking.
if (!initialized) {
synchronized (this) {
if (!initialized) {
T t = delegate.get();
value = t;
initialized = true;
return t;
}
}
}
return value; …Run Code Online (Sandbox Code Playgroud) 我正在尝试实现LZ77的快速版本,我有一个问题要问你关于并发编程的问题.
现在我有一个final byte[] buffer和final int[] resultHolder两个相同的长度.该计划执行以下操作:
主线程写入所有缓冲区,然后通知线程并等待它们完成.
单个工作线程处理缓冲区的一部分,将结果保存在结果持有者的同一部分中.工人的部分是独家的.之后,通知主线程并且工作人员暂停.
当所有工作者都暂停时,主线程读取resultHolder中的数据并更新缓冲区,然后(如果需要)该过程再次从第1点开始.
manager(主线程)中的重要事项声明如下:
final byte[] buffer = new byte[SIZE];
final MemoryHelper memoryHelper = new MemoryHelper();
final ArrayBlockingQueue<Object> waitBuffer = new ArrayBlockingQueue<Object>(TOT_WORKERS);
final ArrayBlockingQueue<Object> waitResult = new ArrayBlockingQueue<Object>(TOT_WORKERS);
final int[] resultHolder = new int[SIZE];
Run Code Online (Sandbox Code Playgroud)
MemoryHelper只是包装一个volatile字段并提供两种方法:一种用于读取它,另一种用于写入它.
Worker的run()代码:
public void run() {
try {
// Wait main thread
while(manager.waitBuffer.take() != SHUTDOWN){
// Load new buffer values
manager.memoryHelper.readVolatile();
// Do something
for (int i = a; i <= b; …Run Code Online (Sandbox Code Playgroud) 我有一个关于 volatile 发生在规则之前的问题,同样的问题也符合监控规则。根据易失性规则,易失性写入发生在任何后续读取之前。
我有以下可变写入和正常后续读取的示例。据我所知,这个易失性写入应该有一个 StoreStore 内存屏障,它将正常的存储刷新到内存中,以便其他进程可以看到它(根据 DougLea 关于 JSR-133 内存模型的食谱)。
所以我的问题是:是否还有一个额外的发生在规则之前,正常存储的操作 1 也发生在正常加载的后续操作 2 之前。
int a;
volatile int x;
public void action1(){
a = 100; --- normal store
x = 123; ---volatile store
}
public void action2{
int k = a; ---normal load
}
Run Code Online (Sandbox Code Playgroud) 我正在处理下面的面试问题,我需要使用两个线程打印出字母和数字.一个打印字母(a,b,c ... z)和其他打印数字(1,2,3 ...... 26).现在我必须以这样的方式实现它,输出应该是:
a
1
b
2
...
...
z
26
Run Code Online (Sandbox Code Playgroud)
所以我想出了下面的代码一,没有同步,但由于某种原因它不打印最后一个字母表 z
class Output {
private static final int MAX = 26;
private static int count = 1;
private static final Queue<Character> queue = new LinkedList<>(Arrays.asList(new Character[] {
'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r',
's', 't', 'u', 'v', 'w', 'x', 'y', 'z'}));
private boolean isAlphabet = true;
public void printAlphabet() {
while (true) {
if …Run Code Online (Sandbox Code Playgroud)