Java中带有等待和通知的死锁-线程不';当notifyAll被调用时,我不会醒来

Java中带有等待和通知的死锁-线程不';当notifyAll被调用时,我不会醒来,java,multithreading,Java,Multithreading,我的程序应该使用线程(为了学习线程)按顺序打印从1到10的数字。 问题是程序陷入了死锁。为什么呢 我创建了10个这样的线程: for (int i = 0; i < 10; i++) { new PrintThread(i).start(); } class PrintThread extends Thread { int curr; static Integer prev; PrintThread(int curr) { this.cu

我的程序应该使用线程(为了学习线程)按顺序打印从1到10的数字。 问题是程序陷入了死锁。为什么呢

我创建了10个这样的线程:

for (int i = 0; i < 10; i++) {
    new PrintThread(i).start();
}
class PrintThread extends Thread {
    int curr;
    static Integer prev;

    PrintThread(int curr) {
        this.curr = curr;
    }

    public synchronized void run() {
        if (prev == null) prev = curr - 1;

        while (curr != prev + 1) {

            System.out.println("Waiting...");

            try { wait(); }
            catch (InterruptedException e){ }

            System.out.println("Woke up!");
        }

        System.out.println(i);
        prev = curr;
        notifyAll();
    }
}
输出

0

Waiting... (9 times)

@SotiriosDelimanolis的评论清楚地解释了这个问题。要指出解决问题的其他方法,请检查以下程序,其中一个程序使用暴力逻辑乘以时间&其他程序等待与您类似的逻辑,而不使用
wait

import java.util.Set;        
public class ThreadChecker {

    public static void main(String[] args) {
        for (int i = 0; i < 10; i++) {
            new PrintThread2(i+1).start();
        }
    }

}

//Brute force method
class PrintThread extends Thread {
    int curr;
    static Integer prev;

    PrintThread(int curr) {
        this.curr = curr;
    }

    public synchronized void run() {
        try {
            Thread.sleep(curr*100);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(curr);
    }
}

//Logic using Thread.sleep
class PrintThread2 extends Thread {
    int curr;
    static Integer prev=0;

    PrintThread2(int curr) {
        this.curr = curr;
    }

    public synchronized void run() {
        while(curr!=prev && curr-prev!=1){
            try {                   
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

         System.out.println(curr);
         prev = curr;      
         //Following is to debug the thread state
         Set<Thread> threadSet = Thread.getAllStackTraces().keySet();
         for(Thread str: threadSet){
            System.out.println("Thread name:"+str.getName()+
                               " ; State:"+str.getState().toString());
         }
    }
}
import java.util.Set;
公共类线程检测器{
公共静态void main(字符串[]args){
对于(int i=0;i<10;i++){
新的PrintThread2(i+1).start();
}
}
}
//蛮力法
类PrintThread扩展线程{
国际货币;
静态整数prev;
打印线程(int curr){
this.curr=curr;
}
公共同步的无效运行(){
试一试{
线程睡眠(电流*100);
}捕捉(中断异常e){
e、 printStackTrace();
}
系统输出打印项次(当前);
}
}
//使用Thread.sleep的逻辑
类PrintThread2扩展了线程{
国际货币;
静态整数prev=0;
PrintThread2(内部电流){
this.curr=curr;
}
公共同步的无效运行(){
while(curr!=prev&&curr-prev!=1){
试试{
睡眠(1000);
}捕捉(中断异常e){
e、 printStackTrace();
}
}
系统输出打印项次(当前);
上一次=当前;
//下面是调试线程状态的步骤
Set threadSet=Thread.getAllStackTraces().keySet();
用于(线程str:threadSet){
System.out.println(“线程名称:”+str.getName()+
“状态:”+str.getState().toString());
}
}
}

所有线程都会同步并自行等待。因此,即使一个线程通知,通知也不会到达任何人,因为其他线程正在等待另一个监视对象(即它们自己)。在这种情况下,所有线程都应该在一个公共监视器对象上同步并等待/通知

您根本不应该在
线程
对象上使用
等待
通知
,因为这实际上可能会导致线程调度iirc中的死锁

作为旁注:不要扩展
Thread
,而是实现一个
Runnable
,并提供该
Runnable
的实例作为
Thread

wait()、notify()和notifyAll()的构造函数的参数,这些都是对象类的方法。这应该由线程之间共享的对象调用。在这里,我编写了一个小的生产者-消费者程序,它可以消除您对wait()和notify()的疑虑

package com.mytest.example;
公共类线程演示{
int消息;
字符串threadName;
布尔消耗;
ThreadDemo(int消息、字符串threadName、布尔值){
这个.setMessage(message);
此.setThreadName(threadName);
这个.setconsumered(已消费);
}
公共静态void main(字符串[]args){
ThreadDemo=newthreaddemo(0,“Main”,true);
线程生产者=新线程(新生产者(演示));
线程消费者=新线程(新消费者(演示));
producer.start();
consumer.start();
试一试{
consumer.join();
}捕捉(中断异常e){
System.out.println(“错误:-”+e.getMessage());
}
System.out.println(“Main()退出”);
}
/**
*@回信
*/
public int getMessage(){
返回消息;
}
/**
*@param message要设置的消息
*/
公共无效设置消息(int消息){
this.message=消息;
}
/**
*@返回threadName
*/
公共字符串getThreadName(){
返回threadName;
}
/**
*@param threadName要设置的threadName
*/
public void setThreadName(字符串threadName){
this.threadName=threadName;
}
/**
*@返回可用的内容
*/
公共布尔值isConsumed(){
消费回报;
}
/**
*@param contentAvailable可设置的内容
*/
已使用公共void集合(已使用布尔值){
这个。消耗=消耗;
}
}
类生成器实现了Runnable{
线程演示;
制作人(ThreadDemo){
this.demo=demo;
}
@凌驾
公开募捐{

对于(inti=1;i而言,原因可能是因为按照线程类,除了join之外,没有其他方法可以保证在特定的时间间隔内执行。 因此,不同的线程在不同的时间间隔被调用,这是基于线程调度器的,我们不再完全控制这些方法

更确切地说,我建议观看以下视频,以了解更多关于线程的信息

希望你能在看了这三个视频后得到你的答案

我尽量简短

import java.util.concurrent.atomic.AtomicInteger;

public class PrintThread implements Runnable {

    public static void main(String[] args) {
        for (int i = 0; i < 10; i++) {
            new Thread(new PrintThread(i)).start();
        }
    }

    private static final AtomicInteger nextToPrint = new AtomicInteger(0);  
    private final int curr;

    public PrintThread(int curr) {
        this.curr= curr;
    }

    @Override
    public void run() {
        synchronized (nextToPrint) {
            while (nextToPrint.get() != curr) {
                try {
                    nextToPrint.wait();
                } catch (InterruptedException e) { /*do nothing*/ }
            }

            System.out.println(curr);   
            nextToPrint.incrementAndGet();
            nextToPrint.notifyAll();
        }
    }
}
导入java.util.concurrent.AtomicInteger;
公共类PrintThread实现可运行{
公共静态void main(字符串[]args){
对于(int i=0;i<10;i++){
新线程(新打印线程(i)).start();
}
}
私有静态最终AtomicInteger nextToPrint=新的AtomicInteger(0);
私人最终国际货币;
公共打印线程(int curr){
this.curr=curr;
}
@凌驾
公开募捐{
已同步(下一次打印){
while(nextToPrint.get()!=curr){
试一试{
nextToPrint.wait();
}catch(InterruptedException e){/*什么也不做*/}
}
系统输出打印项次(当前);
下托普里
import java.util.concurrent.atomic.AtomicInteger;

public class PrintThread implements Runnable {

    public static void main(String[] args) {
        for (int i = 0; i < 10; i++) {
            new Thread(new PrintThread(i)).start();
        }
    }

    private static final AtomicInteger nextToPrint = new AtomicInteger(0);  
    private final int curr;

    public PrintThread(int curr) {
        this.curr= curr;
    }

    @Override
    public void run() {
        synchronized (nextToPrint) {
            while (nextToPrint.get() != curr) {
                try {
                    nextToPrint.wait();
                } catch (InterruptedException e) { /*do nothing*/ }
            }

            System.out.println(curr);   
            nextToPrint.incrementAndGet();
            nextToPrint.notifyAll();
        }
    }
}