gpt4 book ai didi

java - Java9及更高版本中线程间通信无法正常工作

转载 作者:行者123 更新时间:2023-12-01 19:41:59 24 4
gpt4 key购买 nike

线程调度程序的幕后从 Java8 到 Java9 发生了一些变化。我正在尝试缩小以下程序中的更改范围。

下面的程序生成 3 个线程,它们并行运行并同步正确传递监视器锁,并打印

Aa0Bb1Cc2Dd3.......Zz25

当前代码已经在所有 Java 版本中正常工作,我不寻求任何优化。

在使用 Object.wait() 传递锁之前,我使用了 Object.notifyAll() (这可能并不总是正确的,但在这种情况下,它在 java 1.8 中没有什么区别)。这就是为什么此代码有两个版本:版本 1 和版本 2。

版本 1 在所有 Java 版本(Java8 及更早版本、Java9 及更高版本)中运行良好。但不是版本 2。当您评论版本 1 并取消评论版本 2 时,就像这样

//obj.wait();//version 1
obj.notifyAll();obj.wait();//version 2

它在 Java8 中运行完全相同,而在 Java9 和更高版本的 JDK 中则不然。它无法获取锁,或者它获取了锁,但条件已被翻转,并且没有轮到线程。

(例如,假设 NumPy 线程完成了其工作,现在唯一可以获取锁并继续进行的线程是 ThreadCapital,但不知何故 isCapital 已变为 false - 这只是一种猜测,无法证明这一点或不确定这是甚至发生)

我几乎没有使用线程的经验,所以我确信我没有利用监视器上的锁,或者即使我有它也应该在所有 JDK 中反射(reflect)相同的情况。除非Java9及更高版本中发生了一些变化。线程调度程序内部有什么变化吗?

    package Multithreading_misc;

public class App {

public static void main(String[] args) throws InterruptedException {

SimpleObject obj = new SimpleObject();
ThreadAlphaCapital alpha = new ThreadAlphaCapital(obj);
ThreadAlphaSmall small = new ThreadAlphaSmall(obj);
ThreadNum num = new ThreadNum(obj);

Thread tAlpha = new Thread(alpha);
Thread tSmall = new Thread(small);
Thread tNum = new Thread(num);

tAlpha.start();
tSmall.start();
tNum.start();

}
}

class ThreadAlphaCapital implements Runnable{
char c = 'A';
SimpleObject obj;

public ThreadAlphaCapital(SimpleObject obj){
this.obj = obj;
}

@Override
public void run() {
try {
synchronized (obj) {
while(c < 'Z')
{
if(!obj.isCapitalsTurn || obj.isNumsTurn)
{
obj.wait();//version 1
//obj.notifyAll();obj.wait();//version 2
}
else
{
Thread.sleep(500);
System.out.print(c++);
obj.isCapitalsTurn = !obj.isCapitalsTurn;
obj.notifyAll();//version 1
//obj.notifyAll();obj.wait();//version 2
}
}
obj.notifyAll();
}

}
catch (InterruptedException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
}

}
class ThreadAlphaSmall implements Runnable{
char c = 'a';
SimpleObject obj;

public ThreadAlphaSmall(SimpleObject obj){
this.obj = obj;
}

@Override
public void run() {
try {
synchronized (obj) {
while(c < 'z')
{
if(obj.isCapitalsTurn || obj.isNumsTurn)
{
obj.wait();//version 1
//obj.notifyAll();obj.wait();//version 2
}
else
{
Thread.sleep(500);
System.out.print(c++);
obj.isCapitalsTurn = !obj.isCapitalsTurn;
obj.isNumsTurn = !obj.isNumsTurn;
obj.notifyAll();//version 1
//obj.notifyAll();obj.wait();//version 2
}
}
obj.notifyAll();
}
}
catch (InterruptedException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
}
}

class ThreadNum implements Runnable{

int i = 0;
SimpleObject obj;

public ThreadNum(SimpleObject obj){
this.obj = obj;
}
@Override
public void run() {
try {
synchronized (obj) {
while(i < 26)
{
if(!obj.isNumsTurn)
{
obj.wait();//version 1
//obj.notifyAll();obj.wait();//version 2
}
else
{
Thread.sleep(500);
System.out.print(i++);
obj.isNumsTurn = !obj.isNumsTurn;
obj.notifyAll();//version 1
//obj.notifyAll();obj.wait();//version 2
}
}
obj.notifyAll();
}
}
catch (InterruptedException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
}
}

class SimpleObject{
public boolean isNumsTurn = false;
public boolean isCapitalsTurn = true;
}

一些注释:

  1. 当从 Java9 及更高版本运行时,这是从 UnNamed 模块运行的

  2. 我并不是说这只发生在 3 个线程上,只是举个例子。顺便说一句,它(过度通知)对于所有 java 版本的两个线程都运行良好

最佳答案

I believe in over notifying.

不清楚你为什么相信这一点,也不清楚你希望从洒水中获得什么notifyAll()整个代码,但现在是成为怀疑论者的时候了。

that may not be correct all the time but in this situation it doesn't make a difference.

嗯,显然,它确实有所作为。

是的,似乎 JVM 等待队列实现的某些方面已经发生了变化,但这并不重要,因为您的代码带有过时的 notifyAll()调用总是被破坏,纯粹是靠运气。

情况其实很容易理解:

  1. 线程 A 更改状态,以便线程 B 可以继续并调用 notifyAll()
  2. 线程 B 和 C 由于 notifyAll() 被唤醒并尝试重新获取锁。哪一个会获胜,尚未确定
  3. 线程 C 获得锁,发现自己不符合条件,并转到 wait()再次,但在你的第二个变体中,它会做一个虚假的 notifyAll()首先
  4. 线程 A 和 B 由于虚假信号而被唤醒 notifyAll() (B 可能已经醒了,但这并不重要)并尝试重新获取锁。哪一个会获胜,尚未确定
  5. 线程 A 获得锁,发现自己不符合条件并转到 wait()再次,但在你的第二个变体中,它会做一个虚假的 notifyAll()首先
  6. 线程 B 和 C 由于虚假信号而被唤醒 notifyAll() (B 可能已经醒了,但这并不重要)并尝试重新获取锁。哪一个会获胜,尚未确定
  7. 参见 3。

正如您所看到的,使用第二个变体,只要 B 永远不会获得锁,您就有一个可能永远运行的潜在循环。您的变体已过时 notifyAll()调用依赖于错误的假设,即如果您通知多个线程,正确的线程最终将收到锁。

使用notifyAll()没有问题在 notify() 的地方是合适的,因为所有表现良好的线程都会重新检查其条件并转到 wait()如果未满足,则再次执行,因此正确的线程(或一个合格的线程)最终将取得进展。但调用notifyAll() before waiting 的行为不佳,可能会导致线程永久地重新检查其条件,而没有轮到符合条件的线程。

关于java - Java9及更高版本中线程间通信无法正常工作,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55070602/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com