在学习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,如下图:
请问这是什么原因导致的呢?
问题出在你的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。 我编写的代码: 但是出现了错误 哪个地方出现了问题呢?
Java中,任何对象都可以作为锁,并且 wait(),notify()等方法用于等待对象的锁或者唤醒线程,在 Java 的线程中并没有可供任何对象使用的锁,所以任意对象调用方法一定定义在Object类中。 wait(), notify()和 notifyAll()这些方法在同步代码块中调用 有的人会说,既然是线程放弃对象锁,那也可以把wait()定义在Thread类里面啊,新定义的线程继承于Thr