Java 多线程中内存一致性错误的真实示例?
在java多线程技术中,它给出了大量内存一致性错误。但我无法复制它。是否有其他方法来模拟内存一致性错误 本教程中提供的示例如下: 假设定义并初始化了一个简单的int字段:Java 多线程中内存一致性错误的真实示例?,java,multithreading,Java,Multithreading,在java多线程技术中,它给出了大量内存一致性错误。但我无法复制它。是否有其他方法来模拟内存一致性错误 本教程中提供的示例如下: 假设定义并初始化了一个简单的int字段: int counter = 0; 计数器字段在两个线程A和B之间共享。假设线程A递增计数器: counter++; 然后,不久之后,线程B打印出计数器: System.out.println(counter); 如果这两条语句是在同一个线程中执行的,则可以安全地假定打印出的值为“1”。但是,如果这两条语句在不同的线程中执
int counter = 0;
计数器字段在两个线程A和B之间共享。假设线程A递增计数器:
counter++;
然后,不久之后,线程B打印出计数器:
System.out.println(counter);
如果这两条语句是在同一个线程中执行的,则可以安全地假定打印出的值为“1”。但是,如果这两条语句在不同的线程中执行,那么打印出来的值很可能是“0”,因为不能保证线程A对计数器的更改对线程B是可见的,除非程序员在这两条语句之间建立了“先发生后发生”关系
这可能会重现问题,至少在我的计算机上,我可以在一些循环之后重现它
计数器
类:
class Holder {
boolean flag = false;
long modifyTime = Long.MAX_VALUE;
}
线程A
将标志设置为真
,并将时间保存到
modifyTime
thread\u B
,读取计数器的标志。如果thread_B
即使在modifyTime
之后仍然得到false
,那么我们可以说我们重现了问题
class Holder {
boolean flag = false;
long modifyTime = Long.MAX_VALUE;
}
public class App {
public static void main(String[] args) {
while (!test());
}
private static boolean test() {
final Holder holder = new Holder();
new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(10);
holder.flag = true;
holder.modifyTime = System.currentTimeMillis();
} catch (Exception e) {
e.printStackTrace();
}
}
}).start();
long lastCheckStartTime = 0L;
long lastCheckFailTime = 0L;
while (true) {
lastCheckStartTime = System.currentTimeMillis();
if (holder.flag) {
break;
} else {
lastCheckFailTime = System.currentTimeMillis();
System.out.println(lastCheckFailTime);
}
}
if (lastCheckFailTime > holder.modifyTime
&& lastCheckStartTime > holder.modifyTime) {
System.out.println("last check fail time " + lastCheckFailTime);
System.out.println("modify time " + holder.modifyTime);
return true;
} else {
return false;
}
}
}
结果
last check time 1565285999497
modify time 1565285999494
这意味着
thread\u B
getfalse
来自计数器在时间15658999497
上的标志,即使thread\u A
在时间15658599994
上也将其设置为true
(提前3毫秒)。所使用的示例太糟糕,无法说明内存一致性问题。要让它工作起来,需要脆弱的推理和复杂的编码。然而,你可能看不到结果。多线程问题是由于计时不正确而导致的。如果有人想增加观察问题的机会,我们需要增加不吉利时机的机会。
下面的程序实现了它
public class ConsistencyIssue {
static int counter = 0;
public static void main(String[] args) throws InterruptedException {
Thread thread1 = new Thread(new Increment(), "Thread-1");
Thread thread2 = new Thread(new Increment(), "Thread-2");
thread1.start();
thread2.start();
thread1.join();
thread2.join();
System.out.println(counter);
}
private static class Increment implements Runnable{
@Override
public void run() {
for(int i = 1; i <= 10000; i++)
counter++;
}
}
}
公共类一致性问题{
静态整数计数器=0;
公共静态void main(字符串[]args)引发InterruptedException{
螺纹1=新螺纹(新增量(),“螺纹1”);
螺纹2=新螺纹(新增量(),“螺纹2”);
thread1.start();
thread2.start();
thread1.join();
螺纹2.连接();
系统输出打印项次(计数器);
}
私有静态类增量实现可运行{
@凌驾
公开募捐{
对于(inti=1;i2++->3
我们需要一种方法使所有3个步骤原子化,即一次只由一个线程执行
解决方案1:已同步
用Synchronized环绕增量。因为计数器是静态变量,所以需要使用类级同步
@Override
public void run() {
for (int i = 1; i <= 10000; i++)
synchronized (ConsistencyIssue.class) {
counter++;
}
}
@覆盖
公开募捐{
对于(inti=1;i我刚才回答了一个关于Java5中一个bug的问题
鉴于这段代码:
public class Test {
volatile static private int a;
static private int b;
public static void main(String [] args) throws Exception {
for (int i = 0; i < 100; i++) {
new Thread() {
@Override
public void run() {
int tt = b; // makes the jvm cache the value of b
while (a==0) {
}
if (b == 0) {
System.out.println("error");
}
}
}.start();
}
b = 1;
a = 1;
}
}
公共类测试{
易失性静态私有inta;
静态私有intb;
公共静态void main(字符串[]args)引发异常{
对于(int i=0;i<100;i++){
新线程(){
@凌驾
公开募捐{
int tt=b;//使jvm缓存的值为b
while(a==0){
}
如果(b==0){
System.out.println(“错误”);
}
}
}.start();
}
b=1;
a=1;
}
}
a
的易失性存储发生在b
的正常存储之后。因此,当线程运行并看到a!=0
时,由于JMM中定义的规则,我们必须看到b==1
JRE中的错误允许线程进入错误
行,并随后得到解决。如果没有将a
定义为volatile
,这肯定会失败有时当我试图重现一些真实的并发问题时,我会使用调试器。
在打印上做一个断点,在增量上做一个断点,然后运行整个过程。
以不同的顺序释放断点会得到不同的结果
可能很简单,但对我来说很有效。请再看一看如何在源代码中引入该示例
<> P>避免内存一致性错误的关键是理解关系发生之前的关系。这种关系仅仅是一个特定语句的内存写入对另一个特定语句可见的保证。
此示例说明了多线程不是确定性的,即不能保证不同线程的操作的执行顺序,这可能会导致在多个运行中出现不同的观察结果。但它没有说明内存一致性错误!
要理解什么是内存一致性错误,首先需要了解内存一致性。最简单的内存一致性模型是由Lamport在1979年提出的。以下是最初的定义
任何执行的结果都是相同的,就像所有进程的操作都是按某种顺序执行的,每个进程的操作都是按其程序指定的顺序出现在该顺序中一样
现在,考虑这个示例多线程程序,请从最近一篇关于顺序一致性的研究论文中查看这个图像。它说明了真正的内存一致性错误可能是什么样子。
public class Test {
volatile static private int a;
static private int b;
public static void main(String [] args) throws Exception {
for (int i = 0; i < 100; i++) {
new Thread() {
@Override
public void run() {
int tt = b; // makes the jvm cache the value of b
while (a==0) {
}
if (b == 0) {
System.out.println("error");
}
}
}.start();
}
b = 1;
a = 1;
}
}