【问题标题】:which thread is blocked waiting for the thread join() to complete in this short example for SurfaceView?在 SurfaceView 的这个简短示例中,哪个线程被阻止等待线程 join() 完成?
【发布时间】:2014-02-24 05:08:17
【问题描述】:

有人可以解释这里发生了什么,因为哪个线程被阻塞了吗?这里的线程对象用于画布和绘制图形,在这个图形线程上调用了join()方法,所以它会继续完成。而另一个线程被阻塞并等待。这是哪个线程?

如果 surfaceView 类在主 UI 线程上运行,那么 UI 将被阻塞,等待图形线程完成。这根本不符合逻辑。所以另一种解释是这里有三个单独的线程。主 UI 线程、调用 join() 的图形线程,以及在图形线程完成时被阻塞的表面视图的第三个线程

实际上是否有三个线程在运行,而 UI 线程不是被阻塞的?

在我的代码中,我没有为 SurfaceView 启动一个新线程。我只实例化了一个 SurfaceView 对象。

在编写此代码时,主 UI 线程似乎被阻塞,等待图形线程完成。

如果 SurfaceView 对象在线程或 AsyncTask 内部实例化,那么它看起来会更清晰。

// 此代码位于 SurfaceView 类内部

@Override
public void surfaceCreated(SurfaceHolder holder) {
    // can also put this code in surfaceChanged
    running = true;
    thread = new Thread(surfaceRunnable);
    thread.start();

}

@Override
public void surfaceDestroyed(SurfaceHolder holder) {
    running = false;
    boolean retry = true;
    while(retry) {
        try {
        thread.join();
        retry = false;
        } catch( InterruptedException e) {

        }
    }

【问题讨论】:

  • 执行thread.join()的线程是被阻塞的线程,而不是thread所代表的线程。
  • 这是否意味着 surfaceDestoryed 是由后台线程而不是主 UI 线程调用的?
  • 如果在部分代码(surfaceCreated()、surfaceDestroyed()、surfaceRunnable.run())中添加日志消息,然后用adb logcat -v threadtime查看logcat输出,可以在后面看到线程ID记录消息。这使得将活动映射到特定线程变得容易。更好的是附加一个调试器,然后查看所有线程是什么以及每个线程在哪里停止 - 然后你不仅会知道什么,但是为什么。
  • 感谢cmets,我现在正在这样做,看看哪个线程在哪里

标签: java android multithreading graphics surfaceview


【解决方案1】:

被阻塞的线程是UI线程。用调试器证明这一点是个好主意。您可以通过查看 SurfaceView.java 的源代码来确认这一点(如果在 Eclipse 中,请在光标位于文本 SurfaceView 时点击 F3)。特别是:

surfaceDestroyed()等回调是从SurfaceView.updateWindow()调用的:

private void updateWindow(boolean force, boolean redrawNeeded) {
    ...
    callbacks = getSurfaceCallbacks();
    for (SurfaceHolder.Callback c : callbacks) {
        c.surfaceDestroyed(mSurfaceHolder);
    }

updateWindow() 是从派生自Handler 的匿名类调用并分配给mHandler:

final Handler mHandler = new Handler() {
    @Override
    public void handleMessage(Message msg) {
        switch (msg.what) {
            case KEEP_SCREEN_ON_MSG: {
                setKeepScreenOn(msg.arg1 != 0);
            } break;
            case GET_NEW_SURFACE_MSG: {
                handleGetNewSurface();
            } break;
            case UPDATE_WINDOW_MSG: {
                updateWindow(false, false);
            } break;
        }
    }
};

请注意,这个mHandler 对象是在构造SurfaceView 对象时构造的,这发生在UI 线程上。并注意Handler 构造函数将Handler 绑定到与当前线程(UI 线程)关联的Looper。所以handleMesssage() 在那个Looper 上运行,这是UI 线程的Looper。因此,updateWindow() 在 UI 线程上被调用。

updateWindow() 在文件中也被多次调用,大多数时候很容易推断出它是从 UI 线程调用的。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-10-17
    相关资源
    最近更新 更多