当前位置: 首页 > 知识库问答 >
问题:

java - Java线程同步中使用wait方法发现结果与预期不符,是什么导致的?

赫连淳
2024-06-22

在学习Java线程的同步时,我写了两个线程对同一个int变量进行增加的例子来练习wait方法,却发现最终结果与预期结果不符,具体如下:

这是我的线程类NumPlus1,在线程中会循环输出当前线程名和变量i:

public class NumPlus1 implements Runnable{    int i = 0;    @Override    public void run(){        Thread now = Thread.currentThread();        while (i < 5){            synchronized (this){                System.out.println(now.getName() + ":" + i++);                try {                    wait(1000);                } catch (InterruptedException e) {                    throw new RuntimeException(e);                }            }        }    }}

这是我的main方法,在main方法里启动了两个名字为“one”和“two”的线程:

    public static void main(String[] args) {        NumPlus1 numPlus = new NumPlus1();        Thread one = new Thread(numPlus, "one");        Thread two = new Thread(numPlus, "two");        one.start();        two.start();    }

按线程类中的while循环,预期结果是one线程和two线程打印信息中的i会按0,1,2,3,4走,但最终结果有时符合预期,有时却会多出了一个5,如下图:

请问这是什么原因导致的呢?

共有1个答案

夹谷茂
2024-06-22

问题出在你的wait(1000)方法的使用上。wait(long timeout)方法用于让当前线程等待,直到其他线程调用此对象的notify()notifyAll()方法,或者经过指定的超时时间。然而,在你的代码中,你并没有在wait()之后检查while循环的条件是否仍然满足,这可能导致线程在i已经增加到5之后仍然打印出i的值。

在Java中,wait()方法会释放锁,并允许其他线程进入同步块。当wait()方法返回时,线程会重新获取锁,但此时while循环的条件可能已经不再满足(即i可能已经大于或等于5)。然而,由于你没有在wait()之后重新检查while循环的条件,线程可能会继续执行并打印出i的值,即使这个值已经超过了4。

要修复这个问题,你应该在wait()之后立即重新检查while循环的条件。通常,我们会在循环中使用while而不是if来确保在条件不再满足时立即退出循环。但在这里,你还需要确保在wait()之后立即检查条件,以防在wait()期间条件已经改变。

但是,由于wait()方法通常用于等待某个条件变为真,而不是简单地等待一段时间,所以在这个例子中,你可能不需要使用wait()方法。如果你只是想让线程暂停一段时间,那么你应该使用Thread.sleep(long millis)方法,而不是wait()方法。

下面是修改后的代码示例,使用Thread.sleep(long millis)代替wait(long timeout)

public class NumPlus1 implements Runnable {    private int i = 0;    private final Object lock = new Object(); // 使用一个单独的锁对象以避免潜在的同步问题    @Override    public void run() {        Thread now = Thread.currentThread();        while (i < 5) {            synchronized (lock) { // 使用单独的锁对象进行同步                System.out.println(now.getName() + ":" + i++);                try {                    Thread.sleep(1000); // 使用Thread.sleep()代替wait()                } catch (InterruptedException e) {                    throw new RuntimeException(e);                }            }        }    }}

注意,我还添加了一个单独的锁对象lock,以避免在多个实例之间共享this作为锁对象时可能出现的潜在同步问题。虽然在这个例子中只有一个NumPlus1实例,但这是一个好的做法,可以提高代码的可读性和可维护性。

 类似资料:
  • 问题内容: 我有以下代码片段: 我期望结果为“ 987654321”,但是我得到“ 123456789”,就像方法setNumber没有任何效果,任何人都可以帮助我理解 问题答案: 您在方法内重新声明phoneNumber变量,使类中的字段处于阴影中,因此对局部变量所做的任何更改都不会在带阴影的类字段中显示。不要这样 摆脱重复变量声明,以便在字段中看到在方法内所做的更改。 例如,更改此: 对此:

  • 问题内容: 下面是一个简单的Java程序。它具有一个称为“ cnt”的计数器,该计数器递增,然后添加到名为“ monitor”的列表中。“ cnt”由多个线程递增,并且值由多个线程添加到“ monitor”。 在方法“ go()”的末尾,cnt和monitor.size()应该具有相同的值,但它们没有相同的值。monitor.size()确实具有正确的值。 如果通过取消注释已注释的同步块之一并注释

  • 问题内容: 我有一个实现可运行的线程类和一个int计数器作为实例变量。两种同步方法add和sub。当我以某种方式运行测试类时,它几次会输出错误的结果。据我了解,当方法同步时,整个对象将被锁定以供其他线程访问,这种逻辑每次我们都应该获得相同的结果正确吗?事实并非如此。我想念什么吗? 我的机器是Windows 7、64位。 测试类 结果 注意: 您可能需要进行几次运行才能产生这种不一致。 问题答案:

  • 问题内容: 大家都警告Java DateFormat不能保证线程安全,并且我从理论上理解这个概念。 但是我无法想象由此导致的实际问题。说,我在一个类中有一个DateFormat字段,并且在多线程环境中该类的不同方法(格式化日期)中使用了相同的字段。 这会导致: any exception like format exception discrepancy in data any other iss

  • 我的问题:三个线程的ID分别是A,B,C;,每个线程将自己的ID值在屏幕上打印5遍,打印顺序是ABCABC。 我编写的代码: 但是出现了错误 哪个地方出现了问题呢?

  • 那么当我使用调用异步方法时会发生什么呢?整个方法的操作会以同步的方式完成吗?例如,如上面所示,我在中有三个异步操作;、&,那么它们是否会以同步方式执行,因为我使用.wait()调用ScanAsync方法?

  • Java中,任何对象都可以作为锁,并且 wait(),notify()等方法用于等待对象的锁或者唤醒线程,在 Java 的线程中并没有可供任何对象使用的锁,所以任意对象调用方法一定定义在Object类中。 wait(), notify()和 notifyAll()这些方法在同步代码块中调用 有的人会说,既然是线程放弃对象锁,那也可以把wait()定义在Thread类里面啊,新定义的线程继承于Thr

  • 问题内容: 我需要使线程顺序。他们需要按以下顺序启动: 当D完成时,C可以完成,然后B,然后是A。 在这种情况下,最好使用线程或?为什么呢? 我的线程需要启动并打印消息,完成后需要打印。 问题答案: 由于您正在等待“其他”线程完成(即完成执行),因此是更好的选择。 的javadoc 简单地说: 等待该线程死亡。 然后,该机制相对简单: 要说明:您需要参考。因此,指向,指向,指向和不指向任何对象(它