该练习与有效Java中的项目78有关。即,我们创建两个线程以并行方式递增公共静态变量并打印出来。目标是为控制台生成一条统一的数字递增行。 AtomicLong用于避免出现竞争状况,但是有一个我无法解释的错误。 即,首先调用
System.out.println(i.getAndIncrement());
JVM不会读取最新的变量值。仅在第二次呼叫时读取。 Please see the console output with inconsistent output marked 有人可以建议我自己学习如何解决此错误吗?现在该阅读JVM规范了吗?
package com.util.concurrency.tick;
import java.util.concurrent.atomic.AtomicLong;
public class AtomicIncrementer implements Runnable {
private String name;
private static final int MAXI = 1000;
private static final AtomicLong i = new AtomicLong(-1);
public AtomicIncrementer(String name){
this.name = name;
}
public void run(){
while(i.get() < MAXI){
System.out.println(name+ ". i = "
+i.getAndIncrement());
}
System.out.println(name+" i = "+i.get());
}
public static void main(String[] args){;
try {
Thread t1 = new Thread(new AtomicIncrementer("A"));
Thread t2 = new Thread(new AtomicIncrementer("B"));
t1.start();
t2.start();
} catch (Exception e) {
}
System.out.println("Two incrementers launched");
}
}
答案 0 :(得分:0)
基本上您没有使用同步。
您可以如下更改run
方法:
public void run(){
synchronized(i){
while(i.get() < MAXI){
System.out.println(name+ ". i = "
+i.getAndIncrement());
}
System.out.println(name+" i = "+i.get());
}
}
此外,您可以参考本教程以获取有关同步的更多信息:https://www.tutorialspoint.com/java/java_thread_synchronization.htm
答案 1 :(得分:0)
这可能是您要尝试执行的操作:)请检查Java文档。您要在2个线程之间进行同步。基本上,每个线程递增一圈,然后将计数器传递给另一个线程。
public class AtomicIncrementer implements Runnable {
private static final int MAXI = 1000;
private static final SynchronousQueue<Long> LONG_EXCHANGER = new SynchronousQueue<>();
private final String name;
private AtomicIncrementer(String name) {
this.name = name;
}
@Override
public void run() {
try {
while (true) {
Long counter = LONG_EXCHANGER.take();
if (counter >= MAXI) {
LONG_EXCHANGER.put(counter);
break;
}
System.out.println(name + ". i = " + (counter + 1));
LONG_EXCHANGER.put(counter + 1);
}
Long counter = LONG_EXCHANGER.take();
System.out.println(name + " final i = " + counter);
LONG_EXCHANGER.put(counter);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
Thread t1 = new Thread(new AtomicIncrementer("A"));
Thread t2 = new Thread(new AtomicIncrementer("B"));
t1.start();
t2.start();
System.out.println("Two incrementers launched");
try {
LONG_EXCHANGER.put(-1L);
t1.join();
System.out.println("T1 ended");
//this is needed for last thread to end
LONG_EXCHANGER.take();
t2.join();
System.out.println("T2 ended");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
答案 2 :(得分:0)
同步不是问题,因为AtomicLong仅用于此目的。
相反,必须在线程切换时将共享字段从&更新到本地线程内存。为此存在volatile
:
private static final volatile AtomicLong i = new AtomicLong(-1);
实际上我并不完全确定!对于long
,情况很明显,
但是只有一个物体;其长字段正在更新。
答案 3 :(得分:0)
您在这里有很多比赛条件。 以下事件顺序将导致您看到结果:
线程A一直运行到i=198
。然后有上下文切换,线程B运行以下命令:
System.out.println(name+ ". i = "
+i.getAndIncrement());
在while循环内。 线程B创建字符串:
“ B。我= 198”
但是在线程B有机会打印该字符串之前,再次有上下文切换到线程A。 因此线程A继续执行,直到打印
A。 i = 204
然后有一个上下文切换到线程B,它从之前停止的位置恢复,该位置正在打印字符串:
B。我= 198
基本上,在获得i
的当前值并打印之前,您已经处于竞争状态。
换句话说,i.getAndIncrement();
是原子操作。
但是
System.out.println(name+ ". i = "
+i.getAndIncrement());
不是原子操作。
您在这里有多项操作。用伪代码:
1. tempInt = i.getAndIncrement();
2. tempString1 = name + ". i = ";
3. tempString2 = convertToString(tempInt);
4. tempString3 = tempString1 + tempString2;
5. print tempString3;
这就是为什么您会有如此混乱的输出:)
如果您想深入了解这些概念,我建议您参加此在线课程: https://www.udemy.com/java-multithreading-concurrency-performance-optimization/?couponCode=CONCURRENCY
它真的很便宜,您可以在几个小时内完成它。但是,对于那些概念和原子类的警告确实有更深入的介绍。这是一笔很棒的时间投资。
在您的情况下,您要做的实际上是使用同步而不是AtomicLong,因为这里的内容是多操作关键部分。
希望对您有帮助。