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

Java线程-非法监视器状态异常

翟兴邦
2023-03-14

我正在尝试实现线程,其中一个线程生成随机数,而另一个线程等待,一旦它生成随机数,它应该通知并等待另一个线程也这样做。我收到了非法的监控状态异常,请帮我指出我的错误。

class Dice
{
    int diceValue;

    public Dice()
    {
        this.diceValue=0;
    }
}
public class DiceGame  implements Runnable
{
    Dice d;

public DiceGame()
{
    this.d=new Dice();
}
public void run()
{
    if(Thread.currentThread().getName().equals("Player 1"))
    {
        Random rg=new Random();
        for(int i=0;i<6;i++)
        {
            synchronized(d)
            {

                d.diceValue=rg.nextInt(6);
                System.out.println(Thread.currentThread().getName()+" dice Value is "+d.diceValue);
                d.notifyAll();
                try 
                {
                    d.wait();
                }

                catch (InterruptedException e) 
                {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
        }
    }
    else if(Thread.currentThread().getName().equals("Player 2"))
    {
        Random rg=new Random();
        for(int i=0;i<6;i++)
        {
            synchronized(d)
            {
                try 
                {
                    d.wait();
                } 
                catch (InterruptedException e) 
                {
                    e.printStackTrace();
                }
                d.diceValue=rg.nextInt(6);
                System.out.println(Thread.currentThread().getName()+"dice Value is ");
                d.notifyAll();
            }
        }
    }
}



public static void main(String []args)
{
    DiceGame dg=new DiceGame();

    Thread tr1=new Thread(dg);
    Thread tr2=new Thread(dg);

    tr1.setName("Player 1");
    tr2.setName("Player 2");

    tr1.start();
    tr2.start();
}
}

共有3个答案

许昆
2023-03-14

您的代码可以通过多种方式进行改进,但是通过一些小技巧,它可以正常工作:

class Dice
{
    int diceValue;

    public Dice()
    {
        this.diceValue=0;
    }
}
public class DiceGame  implements Runnable
{
    Dice d;

    public DiceGame()
    {
        this.d=new Dice();
    }
    @Override
    public void run()
    {
        if(Thread.currentThread().getName().equals("Player 1"))
        {
            final Random rg=new Random();
            for(int i=0;i<6;i++)
            {
                synchronized(d)
                {

                    d.diceValue=rg.nextInt(6);
                    System.out.println(Thread.currentThread().getName()+" dice Value is "+d.diceValue);
                    d.notifyAll();

                    try
                    {
                        d.wait();
                    }

                    catch (final InterruptedException e)
                    {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                }
            }
        }
        else if(Thread.currentThread().getName().equals("Player 2"))
        {
            final Random rg=new Random();
            for(int i=0;i<6;i++)
            {
                synchronized(d)
                {
                    try
                    {
                        d.wait();
                    }
                    catch (final InterruptedException e)
                    {
                        e.printStackTrace();
                    }

                    d.diceValue=rg.nextInt(6);

                    System.out.println(Thread.currentThread().getName()+" dice Value is "+d.diceValue);
                    d.notifyAll();
                }
            }
        }
    }



    public static void main(final String []args) throws InterruptedException
    {
        final DiceGame dg=new DiceGame();

        final Thread tr1=new Thread(dg);
        final Thread tr2=new Thread(dg);

        tr1.setName("Player 1");
        tr2.setName("Player 2");

        tr2.start();
        Thread.sleep(100);
        tr1.start();
    }
}

我没有非法Monitorstate异常,但第一个线程被永久锁定。基本上,问题是第一个线程掷骰子,并在第二个线程实际启动并等待骰子之前调用d.notifyAll。这是通过先启动线程2,然后等待一点,再启动线程1来天真地解决的。

您还可以考虑:

  • 对大括号 { } 使用 Java 约定
  • rg.nextInt 给出的值介于 0..5 之间,而不是 1..6
  • 之间
  • 根据线程的名称使线程代码以不同的方式工作是坏主意。在 OOP 中,不同的行为用后代类表示。

我猜你希望有一个通用的解决方案来让多个玩家掷骰子。这个问题不一定是需要并发编程的问题,因为掷骰子是在玩家之间串行进行的。你当然可以让玩家作为线程,但是在任何时间点只有一个线程是活动的。在使用线程的情况下,你应该实现自己的调度逻辑,以确保线程的一个接一个的调度一致。使用监视器(例如synchornize(d))不能提供任何排序保证,它只是为了保证在任何时间点最多有一个线程可以访问骰子而构建的。

具有任意数量玩家但没有线程的解决方案(这毕竟不是并发问题)显示了这种行为:

import java.util.Random;

class Dice {
    private final Random rg=new Random();
    private int diceValue=1;

    public void roll() {
        diceValue=rg.nextInt(6)+1;
    }

    @Override
    public String toString() {
        return "value="+diceValue;
    }
}

public class Player extends Thread {
    Dice dice;
    int rollsLeft=6;

    public Player(final Dice dice) {
        this.dice=dice;
    }

    @Override
    public void run() {
        while (rollsLeft>0) {
            synchronized(dice) {
                // dice obtained
                dice.roll();
                System.out.println(Thread.currentThread().getName()+" rolled "+dice);
            }
            // dice released
            rollsLeft--;

            // just wait a little to make it slower and let other threads to join 
            try {
                Thread.sleep(100);
            } catch (final InterruptedException e) {
                // ignore
            }
        }
    }

    public static void main(final String []args) throws InterruptedException {
        final Dice dice=new Dice();
        final Player player1=new Player(dice);
        final Player player2=new Player(dice);

        player1.start();
        player2.start();
    }
}

这给出了:

Thread-0 rolled value=1
Thread-1 rolled value=6
Thread-0 rolled value=2
Thread-0 rolled value=4
Thread-1 rolled value=2
etc...

如您所见,顺序(即player1、player2、player1和player2)没有保证。

import java.util.ArrayList;
import java.util.List;
import java.util.Random;

class Dice {
    private final Random rg=new Random();
    private int diceValue=1;

    public void roll() {
        diceValue=rg.nextInt(6)+1;
    }

    @Override
    public String toString() {
        return "value="+diceValue;
    }
}

public class Player {
    Dice dice;
    String player;

    public Player(final Dice dice,final String player) {
        this.dice=dice;
        this.player=player;
    }

    public void roll() {
        dice.roll();
        System.out.println(player+" rolled "+dice);
    }

    public static void main(final String []args) throws InterruptedException {
        final Dice dice=new Dice();
        final List<Player> players=new ArrayList<Player>();
        players.add(new Player(dice,"Ann"));
        players.add(new Player(dice,"Ben"));
        players.add(new Player(dice,"Cecil"));
        players.add(new Player(dice,"Denise"));

        for (int rounds=0;rounds<6;rounds++) {
            System.out.println("---");
            for (final Player player:players) {
                player.roll();
            }
        }
    }
}

这会给你预期的输出,即Ann、Ben、Cecil、Denise有6轮掷骰子。

阙俊友
2023-03-14

我想,问题是你在等待自己之前通知了所有其他线程。

 d.notifyAll();
 try 
 {
    d.wait();
 }

参考本帖:https://stackoverflow.com/a/828341/5602214

汪玮
2023-03-14
        synchronized(d)
        {
            try 
            {
                d.wait();

每当您看到对wait的无条件调用时,您就知道那里有一个bug。在等待之前,您必须确保等待的事情尚未发生。这就是您输入synchronized块的原因,对吗?

< code > wait /< code > notify 机制的要点是,您可以自动释放锁并等待通知。如果您在调用< code>wait之前不检查谓词(您正在等待的东西),这是不可能的。

这里,同步块对于在呼叫等待时保持监视器是必要的。

对,因为除非你在一个同步块中,否则你无法判断你正在等待的事情是否已经发生了。而且因为你必须在等待它之前检查它是否已经发生了,所以你只能从一个同步块中调用etc。但是你没有检查!你理解需求,但不了解它的基本原理,所以你正式满足了它,但仍然设法制造了需求旨在防止的问题!

 类似资料:
  • 问题内容: 如何将轮询线程传递给另一个线程进行处理。程序执行在具有主方法和线程池的控制器类中: 主类控制器 具有轮询类的线程的方法 具有proc类的线程的方法 轮询类和控制器类 我的任务和问题是: 1.控制器应同时处理轮询器和处理器线程,并且应仅调用轮询器和处理器线程 2.现在我的问题是如何使轮询线程等待3秒并并行通知处理器。 我得到如下错误: 这里如何实现异步处理? 问题答案: 你需要阅读的东西

  • 我有一段代码(简化): 其中reentrantLockObject是java。util。同时发生的锁。可重入锁定。有时我会得到非法的监控。在check和unlock()调用之间释放了锁。如何防止这种异常?

  • 我的应用程序处于生产状态,它支持从API 8到23。我最近更新了应用程序,使其具有使用导航抽屉的材料设计。此版本支持从 14 到 23。 该应用程序在API 21[Lollipop]及以上版本上运行良好,但有4.4“java illegalstateexception android.support.v7.app.AppCompatDelegateImplV7.createSubDecor”的崩溃

  • Java SE6文档中的ThreadPoolExecutor类具有以下方法: 返回正在积极执行任务的线程的大致数目。 这里近似和积极执行是什么意思? 在调用之前、期间和之后,是否保证 null 我已经研究了线程池执行器监视需求,以及如何在java中判断线程池中是否有可用的线程,但它们没有回答我的查询。

  • 在Java尝试使用一个简单的计数器来实践生产者和消费者。不知道为什么我会在这段代码上得到一个非法的监视器状态异常。 我有计数器rest和计数器Consumer方法,它们在自己的线程中运行。计数器本身是一个静态的int volatile字段。counter类还为您提供了一个锁 如果将wait naotify更改为: 代码起作用了。dosen't wait()和notify()自动获得锁synchro

  • 我已经建立了一个应用程序,我使用了Okhttp和改装。除了一些不规则的有线行为外,一切正常。在这个新闻应用程序中,我触发了多个部分的提要下载请求。时间到时间请求从不返回任何响应(甚至不会抛出异常),它只是被卡住了,在Logcat中,我看到了连续的垃圾收集。 我试图找到潜在的嫌疑人,但什么都找不到。从ddms中的“线程”中,我唯一能看到的是很多线程都处于“监视”状态(请参见屏幕截图) 编辑:另一个用