我试图通过两个线程重复使用wait和notify来打印偶数和奇数。但是,我已经完成了网站上给出的所有实现。虽然作为第一次多线程开发人员,我试图自己做,但我无法得到理想的结果。在这里,我将我的代码粘贴在下面:请您查看并回复我犯错误的更正和解释。
package com.test.printEvenOdd;
public class PrintOddEvenNumbers {
public static void main(String[] args){
String s = new String("");
EvenThread t1= new EvenThread(s);
OddThread t2= new OddThread(s);
Thread th1 = new Thread(t1);
Thread th2 = new Thread(t2);
th1.start();
th2.start();
}
}
class EvenThread implements Runnable{
String s;
EvenThread(String s){
this.s= s;
}
@Override
public void run() {
synchronized(s){
for(int i=1;i<=10;i++){
if(i%2==0){
try {
Thread.sleep(50);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(i);
s.notify();
}
try {
s.wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
}
class OddThread implements Runnable{
String s;
OddThread(String s){
this.s= s;
}
@Override
public void run() {
synchronized(s){
for(int i=1;i<=10;i++){
try {
Thread.sleep(50);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
if(i%2==1){
System.out.println(i);
s.notify();
}
try {
s.wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
}
答案 0 :(得分:2)
您的问题是锁定 太强保守/限制:
你把锁放在整个循环周围;两个线程。
因此,一个线程进入其循环;但很快它就无法进步。因为它需要其他线程来进步。但是第二个线程甚至无法启动 - 因为它可以完全进入其循环!
换句话说:为了取得进步;两个线程都需要能够进入各自的循环;并取得足够的进展,以便其他线程可以完成下一步。
就像建造一个只有两个人可以一起出去的房间;但是你只允许一个人进入那个房间。
欢迎使用多线程编程;你刚刚创建了第一个死锁。
并记录:重新安排锁;确保你获得正确的信号;这样wait / notify可以按预期工作。
最后:如果你仔细看看你的代码;你会发现你复制了很多的代码。这总是一个坏主意。相反:尝试确定哪些部分真的不同;和其他任何东西......应该在源代码中完全存在一次。因此,作为另一个练习:当您重新安排代码以使其完成预期的操作时 - 尝试重构它,以便最大限度地减少代码重复的数量。我保证,这将是值得花时间的练习!
答案 1 :(得分:1)
您应该在“if”块内移动“wait()”。其他线程将在没有通知其他等待线程的情况下等待,并且它们都将等待。
if(i%2==0){
synchronized(s){
System.out.println(i);
try {
s.notify();
s.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
代码存在问题。没有必要睡觉。正如之前的回复中所提到的,您正在急切地进行同步,这是不必要的。无法保证是否首先启动线程或首先启动奇数线程。这取决于哪个线程首先获得锁定。最后,一个线程将永远等待,因为另一个线程已经出现,之后没有人会通知。任何wait()代码都应该处理here
的虚假唤醒答案 2 :(得分:1)
您的初始代码存在许多问题。有关它们的解释,请参阅GhostCat的答案。一般来说,这种计算对于多线程来说并不是很好,因为你(显然)想要按顺序打印数字。但是,考虑到这种需求并希望使用2个线程交错来实现这一点,您可以按如下方式进行。请注意,此解决方案仍存在一些问题。线程依赖于已执行的不同线程,以便能够达到它自己的结束条件,这意味着如果您只为奇数(或偶数)数字创建一个,则进入无限循环。 / p>
import java.util.Objects;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.function.IntPredicate;
public class Foo {
public static void main(String[] args) {
// an executor service will handle the thread pool and scheduling
ExecutorService pool = Executors.newFixedThreadPool(2);
pool.submit(new NumberPrintAndIncrement(i -> i % 2 != 0));
pool.submit(new NumberPrintAndIncrement(i -> i % 2 == 0));
// you want to shut down the pool when the threads are done
pool.shutdown();
}
}
final class NumberPrintAndIncrement implements Runnable {
// Need a shared lock for accessing and updating the current number
private static final Object LOCK = new Object();
// The number is shared between threads so it needs to be volatile
private static volatile int number = 1;
// Instance variable for letting a particular runnable know if it should
// print the number in it's current state
private final IntPredicate predicate;
NumberPrintAndIncrement(IntPredicate predicate) {
this.predicate = Objects.requireNonNull(predicate);
}
@Override
public void run() {
while (number < 10) {
// this could run at any point and any number of times, but
// that doesn't matter since it is just doing a quick check and
// a possible update. If the number doesn't satisfy the predicate,
// this will just be a no-op. Having a predicate means
// you don't have to rely on wait and notify to try and
// achieve interleaving the number output properly which
// is good due to the liveness problem Rajesh mentioned.
synchronized (LOCK) {
if (predicate.test(number)) {
System.out.println(number);
number++;
}
}
}
}
}
答案 3 :(得分:1)
为了更好地理解发生了什么,让我们来看看每个线程中发生的步骤。
public class PrintOddEvenNumbers {
public static void main(String[] args){
String s = new String("");
EvenThread t1= new EvenThread(s);
OddThread t2= new OddThread(s);
Thread th1 = new Thread(t1);
Thread th2 = new Thread(t2);
th1.start();
th2.start();
}
}
class EvenThread implements Runnable{
String s;
EvenThread(String s){
this.s= s;
}
@Override
public void run() {
synchronized(s){
for(int i=1;i<=10;i++){
System.out.println("EvenThread i: " + i);
if(i%2==0){
try {
Thread.sleep(50);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(i);
System.out.println("EvenThread notify");
s.notify();
}
try {
System.out.println("EvenThread waiting..");
s.wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
}
class OddThread implements Runnable{
String s;
OddThread(String s){
this.s= s;
}
@Override
public void run() {
synchronized(s){
for(int i=1;i<=10;i++){
System.out.println("OddThread i: " + i);
try {
Thread.sleep(50);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
if(i%2==1){
System.out.println(i);
System.out.println("OddThread notify");
s.notify();
}
try {
System.out.println("OddThread waiting..");
s.wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
}
这将打印:
EvenThread i: 1
EvenThread waiting..
OddThread i: 1
1
OddThread notify
OddThread waiting..
EvenThread i: 2
2
EvenThread notify
EvenThread waiting..
OddThread i: 2
OddThread waiting..
一个简单的解释:
i
时,waits
的{{1}}将被释放。s
时,i
的{{1}}也会被释放。现在你有两个线程等待被唤醒(死锁)。
这是因为要使用waits
s
和notify
唤醒其他等待线程需要满足的条件。
这不是唯一的问题,但也存在一些基本问题。
i%2==1
多余。