[英]Print Even Odd number using two threads
我试图通过两个线程重复使用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();
}
}
}
}
}
您的问题是您的锁定 过于保守/严格:
您将锁放在整个循环中; 对于两个线程。
因此,一个线程进入了它的循环。 但很快就无法进行。 因为它需要其他线程才能进行。 但是第二个线程甚至无法启动-因为它完全可以进入其循环!
换句话说:为了进步; 两个线程都必须能够进入各自的循环; 并取得足够的进展,以便其他线程可以执行下一步。
这就像建立一个只有两个人可以一起离开的房间; 但是您只允许一个人进入那个房间。
欢迎使用多线程编程; 您刚刚创建了第一个死锁 。
并记录在案:重新布置锁时; 确保正确地发出信号; 这样等待/通知可以按预期工作。
最后:如果您仔细看一下代码; 您会发现您重复了很多代码。 那总是一个坏主意。 相反:尝试找出哪些部分确实不同; 其他...在源代码中应该只存在一次 。 因此,作为另一项练习:当您重新排列代码以使其能够执行预期的工作时-请尝试是否可以对其进行重构,以使代码重复量最小化。 我向您保证,这是值得您花时间的练习!
您应该将“ wait()”移至“ if”块内。 其他线程将进入等待状态,而不会通知其他正在等待的线程,并且两个线程都将处于等待状态。
if(i%2==0){
synchronized(s){
System.out.println(i);
try {
s.notify();
s.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
代码有问题。 无需睡觉。 如先前的答复中所述,您过于急切地进行了同步,这是不必要的。 无法保证偶数线程将首先启动还是奇数线程将首先启动。 它取决于哪个线程首先要获取锁。 最后,一个线程将永远等待,因为另一个线程将已经出来,此后没有人通知。 并且任何wait()代码都应处理此处解释的虚假唤醒
您的初始代码有很多问题。 有关它们的说明,请参见GhostCat的答案。 通常,这种计算不适用于多线程,因为(显然)您希望顺序打印数字。 但是,鉴于这一愿望并希望使用2个线程交织来做到这一点,您可以按照以下方式进行操作。 请注意,此解决方案仍然存在一些问题。 该线程依赖于已执行的另一个线程能够达到其自身的结束条件,这意味着,如果仅为奇数(或偶数)创建一个,则将陷入无限循环。
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++;
}
}
}
}
}
为了更好地了解正在发生的事情,让我们看一下每个线程中发生的步骤。
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
的2时 ,它waits
s
释放。 i
时,它也waits
s
被释放。 现在,您有两个线程都在等待被唤醒( 死锁 )。
发生这种情况的原因是,为了满足使用notify
唤醒另一个等待线程的条件,即i%2==1
和i%2=0
。
这不是唯一的问题,但也存在一些基本问题。
synchornize
成为冗余。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.