public class Test {
public static void main(String[] args) throws Exception {
Executor executor = new Executor();
new Thread(executor::write).start();
new Thread(executor::read).start();
Thread.sleep(500000);
}
static class Executor {
private int MAX_TIMES = 10000;
private boolean hasValue = false;
void write() {
for (int i = 0; i < MAX_TIMES; i++) {
while (hasValue) {}
System.out.println("Write, " + i);
hasValue = true;
}
}
void read() {
for (int i = 0; i < MAX_TIMES; i++) {
while (!hasValue) {}
hasValue = false;
System.out.println("Read, " + i);
}
}
}
}
我有这么一段代码,原本目的是两个线程交换执行、write线程执行一次,read线程执行一次,但hasValue并不是volatile类型,所以会发生死锁
我有两个问题:
1. 如果读写都是cache line,那为什么不是刚执行就发生死锁,而是执行一会,大概会交替执行100次左右才死锁。
2. 输出结果很奇怪,为什么不是write 0=> read 0 => write 1=>read 1.......这样下去,实际输出结果是这样的
Write, 0
Write, 1
Read, 0
Read, 1
Write, 2
Read, 2
Write, 3
Write, 4
Read, 3
Read, 4
Write, 5
Write, 6
Read, 5
Read, 6
Write, 7
Write, 8
Read, 7
Read, 8
Write, 9
Write, 10
Read, 9
Read, 10
Write, 11
Write, 12
如果读写都是cache line,那为什么不是刚执行就发生死锁,而是执行一会,大概会交替执行100次左右才死锁。
2. 输出结果很奇怪,为什么不是write 0=> read 0 => write 1=>read 1.......这样下去,实际输出结果是这样的
第二个问题println不是输出换行吗,那当然是每次循环都换行输出,我不是很理解你为什么会认为会那样子输出
因为说起来是多线程其实都是在main
线程下的子线程,jvm是一个单进程的,其所有的线程都在其进程下面,这里普及一下,何为多进程,比如windows,你可以听着歌写着代码,这就是多进程,jvm是但进程,其就是一个虚拟机,在其main
线程下才会开启多线程,这么说也就是虽然是多线程其实是交替执行的,假如时间的最小单位是1,呢么在1这个点a线程运行,在2这个点就有可能是b线程运行,其本身原理就是交替执行,只不过其交换速度很快,所以你才会觉得是2个线程同时再跑,其实说交替不合适,因为1这个时间点a线程执行,2这个时间点a线程依然可能执行,注意是可能,a,b线程会抢线程资源,谁抢到就执行谁,就是这个意思,所以,当a,b两个线程同时运行,注意这里是同时而不是同步,因为线程是不可能同步的,在一个最小时间点,只可能运行一个线程,假如a虽然先抢到线程资源(比如1这个时间点a抢到),但是他比较点背,在随后的(2,3,4时间点)都是b抢到线程资源,呢么就会出现b虽然是后入线程,但是后来者居上了.所以其本身就没有顺序一说,出现的顺序也是无规则的,你每一次运行,规则都会不一样.
不客气的说 这是我见过最奇葩多线程测试代码,为什么这么写? 抱歉哈
void write() {
for (int i = 0; i < MAX_TIMES; i++) {
while (hasValue) {}
System.out.println("Write, " + i);
hasValue = true;
}
}
void read() {
for (int i = 0; i < MAX_TIMES; i++) {
while (!hasValue) {}
hasValue = false;
System.out.println("Read, " + i);
}
}
执行次数过多的情况下,两个线程可能同时拥有 while(true),可能不进入最后的一个睡眠状态,造成死锁。
首先代码执行分为 while判断 、空代码块儿执行,hasValue 的赋值 和 System.out.println 四个个步骤看
问题一:个人认为不是死锁,应该是一个线程长时间持有cpu,在进行无限循环执行空代码块儿
问题二:是由于
1,当 write 线程 抢到cup时,执行了 输出和赋值操作后,被read线程抢到cpu
2,read线程抢到cpu, 执行了hasValue 赋值,之后又被write抢到cpu
3, write 线程 抢到cup后,又执行了输出操作
所以看到两次write线程的输出,同理read线程的输出也可以理解