当前位置: 首页 > 面试题库 >

线程在notifyall()之后不返回

长孙智刚
2023-03-14
问题内容

我是Java多线程的新手。我已经进行了一些研究,阅读了教程并进行了测试,但是我仍然遇到这个问题。基本上,我正在设置游戏的骨架,我想拥有一个主要的活动类,一个包含方法的线程类,执行各种慢速操作(读取文件并将内容解压缩到缓冲区),并具有一个线程是游戏循环对UI操作的反应。

首先,我有main活动类,该类实例化并启动一个单独的线程:

public class ExperimentsActivity extends Activity {

// This is just a container class with some member data such as ByteBuffers and arrays
TestClass tclass = new TestClass(this);

// Main looping thread
MainLoopThread loop;
Thread mainLoop;

// Start the main looping thread which will trigger the engine's operations
loop = new MainLoopThread(tclass);
mainLoop = new Thread(loop);
mainLoop.start();
loop.setRunning(true);

(...)
}

然后,我有了MainLoopThread实现游戏逻辑线程的类:

public class MainLoopThread implements Runnable  {

public boolean running;
private TestClass baseData;

// Thread for data loading/unpacking ( CLASS DEFINITION BELOW )
GFXUnpack dataUnpack;
Thread dataUnpackThread;

public MainLoopThread( TestClass testClassStructure ) {
    running = false;
    baseData = testClassStructure;
}

public void setRunning ( boolean run ) {
    if ( run == true )
    {
        // Launch the thread which manages loading and unpacking graphics
        dataUnpack = new GFXUnpack(baseData.filepack[0]);
        dataUnpackThread = new Thread(dataUnpack);
        dataUnpackThread.start();
        dataUnpack.setRunning(true);
        fileOpened = false;

        // Open the GFX packet file
        try {
            synchronized (this) {
                dataUnpack.setOperation(2);                     
                Log.d("MainLoopThread", "File opening : waiting...");
                while ( dataUnpack.fileOpened == false ) {
                    wait();
                }
                Log.d("MainLoopThread", "File opening wait completed");
            }

            if ( dataUnpack.outCode == -1 ) 
                    Log.d("MainLoopThread", "File opening error !!");
                else fileOpened = true;
                    Log.d("MainLoopThread", "File opening completed");
            } 
            catch ( Exception exp ) {
                Log.d("MainLoopThread", "File opening code exception !!" + exp);
            }
        }
        else if ( dataUnpack.running == true ) dataUnpack.setRunning(false);              running = run;
    }

    // ------------------------------------
    // Here is the main looping thread. All the events related to loading 
    // and unpacking graphics go here
    public void run() {
        while (running) {
            synchronized (this) {
              // ------ Read a GFX packet and update texture pixels
              if ( fileOpened == true ) {
                  try {
                      // ( Do some stuff... )
                      wait();
                  } catch ( Exception exp ) {
                  Log.d("MainLoopThread", "Exception thrown !! " + exp );
              }
          }
      } // ( Thread-out code removed. Anyway, it never passed here )
  }

最后,GFXUnpack线程类(其中包含打开SD卡上的文件的代码)读取其中的内容并将其写入缓冲区:

public class GFXUnpack implements Runnable {
// -------------    
public boolean running = false;
private Filedata fdata;
private int operation = 0, parameter = 0;
public boolean fileOpened;
public int outCode;  // Used to signal the caller about the outcome of the operation
// ------------------------------
public GFXUnpack ( Filedata packetDataStructure ) {
    this.fdata = packetDataStructure;
}
// --------
public void setRunning ( boolean run ) {
    running = run;   operation = 0;  fileOpened = false;            
    outCode = 0;      parameter = 0;
}
// --------
public void setOperation ( int op ) {
    operation = op;
}
// ---
public void setOperation ( int op, int parm ) {
    operation = op;
    parameter = parm;
}
// ---------    
public synchronized void run() {
    while (running) {
        try {
      switch ( operation ) {
                case ( 2 ) :  // Open the gfx data file
                        ( ...do stuff... )
                        break;                  
                    }
                    // ---------
                    try {
                           ( ...Do some stuff here... )
                           Log.d("GFXUnpack", "Mapping file");
                           ( ...Do some stuff here... )
                           Log.d("GFXUnpack", "Mapped file");
                           fileOpened = true;
                           outCode = 1;
                    } catch ( Exception e ) {
                        Log.d("GFXUnpack", "File opening exception !! " + e);
                        outCode = -1;
                    }
                    finally {
                        operation = 0;       parameter = 0;
                        notifyAll();
                        Log.d("GFXUnpack", "Notified file opening");
                    }
                }
                break;
    // ----------------
            }
            // ----- Other cases here... 
        } finally {
        }
    }
}

当我运行以上命令时,调试器的输出为:

MainLoopThread文件打开:等待中…
GFXUnpack映射文件
GFXUnpack映射文件
GFXUnpack通知文件打开

然后,该应用程序挂起,我不得不强制关闭它。我以为,由于我(在该块中)调用notifyAll()了该run()方法,因此调用程序线程(MainLoopThread)将继续运行,并且会看到调试器消息“文件打开完成”,但是该应用程序挂起了。GFXunpack``finally{}

有谁知道为什么会这样吗?


问题答案:

MainLoopThread实例上等待this(的实例MainLoopThread),并且GFXUnpack在实例通知this(的实例GFXUnpack)。因此,通知程序不会通知正在等待的线程。

这两个对象必须使用 相同的对象实例
来等待和通知。甚至更好的是,您应该使用java.util.concurrent软件包中的更高级别的抽象,例如Semaphores,CountDownLatches等,而不是这些难以使用的低级别方法。

此外,wait()应始终在循环中调用该循环,以检查是否实现了唤醒所需的条件,如果由于虚假唤醒而没有唤醒,则再次开始等待。



 类似资料:
  • 问题内容: 我想要一个布尔值来通知系统的某些部分特定服务已启动。 由于某些奇怪的原因,我得到了错误。 奇怪的是notifyAll()在一个同步块内,该块控制着我调用notifyAll()的对象。 我的课是这样开始的: 我正在开发一个android应用程序。我认为它不会影响任何事情,但是我会在注释中补充这个问题,以防影响Java的工作方式。 如果对象锁定在同步块中,为什么会出现异常? 问题答案: 线

  • 问题内容: 我有很多代码, 调用时处理器很小,有时会导致性能问题(100毫秒),因此我想知道当前等待对象释放的线程是什么。 我想在调用之前打印所有等待对象释放的线程 问题答案: 是否所有线程都在资源上等待相同的条件?如果是,则可以尝试替换,尽管实际上不建议这样做。AFAIK,无法“检索”在给定对象上等待的所有线程的列表(尽管您可以通过编程方式获取进程的线程转储并查看线程,但是我敢肯定那不是您拥有的

  • 我是spring batch的新手。我已经使用多个线程从spring创建并成功执行了作业,它工作得很好,只是当程序执行完成时,程序流不会结束/停止。i、 即使main方法的最后一条语句被执行,程序也不会退出。我不确定它是否一直在等待线程完成,或者是什么。有人能给我一些建议吗?“下面是我的作业配置文件 下面是启动器代码 如上所述,代码在5个不同的线程中为任务“hello”运行,为任务“world”运

  • 本文向大家介绍Java多线程基础 线程的等待与唤醒(wait、notify、notifyAll),包括了Java多线程基础 线程的等待与唤醒(wait、notify、notifyAll)的使用技巧和注意事项,需要的朋友参考一下 本篇我们来研究一下 wait() notify() notifyAll() 。 DEMO1: wait() 与 notify() DEMO1 输出: 注意: 使用 wait

  • 考虑以下代码 结果如下: notifyAll()在13毫秒时被调用。但是,控件仅在10016毫秒时退出等待()。 从上面给出的代码中可以看出,在进行了通知()调用之后,等待()调用似乎没有立即结束。 但是,包括Java API在内的所有文档都指定,调用wait()的方法应该在notify()调用之后立即获得锁。 如果在调用通知()时等待()不会结束,那么对通知()的需求就会变得无效,因为即使没有调

  • 若要恢复挂起在给定对象上的线程,其他一些线程必须在引用相同对象的同步语句或方法中执行预先定义的方法notify。和wait一样,notify也没有参数。作为对notify调用的响应,语言运行时系统选择挂起在对象上的任意线程并使其可运行。如果没有这样的线程,那么通知是一个无操作。就像在Mesa中一样,有时唤醒在给定对象中等待的所有线程可能是合适的;Java为此提供了一个内置的notifyAll方法。