同步、锁定和等待阻塞主线程 UI

Synchronized, lock and wait blocking main UI thread

我做了一个简单的 TaskManager 尝试管理我的项目所需的 Runnable 队列。但是,对于一个简单的场景,添加一个新的 Runnable 会阻塞调用线程(主 UI 线程)。

在当前任务未完成的情况下添加新任务时会发生这种情况。 您可以在下面找到重现它的场景。 我不清楚为什么,以及如何防止这种情况。

这是任务管理器class:

public class TaskManager {

    private Queue<Runnable> executionQueue;
    private final Object lock = new Object();

    public TaskManager() {
        executionQueue = new LinkedList<>();
        startListening();
    }

    public void executeAsyncWithCompl(Runnable runnable, CompletionHandler completionHandler) {
        Runnable runnableWithCompl = new RunnableWithCompl(runnable, completionHandler);
        executeRunnable(runnableWithCompl);
    }

    private void executeRunnable(Runnable runnable) {
        synchronized (lock) {
            executionQueue.add(runnable);
            lock.notifyAll();
        }
    }

    public void release() {
        synchronized (lock) {
            lock.notify();
        }
    }

    private void startListening() {
        Thread executionThread = new Thread(new Runnable() {
            @Override
            public void run() {
                listenTasks();
            }
        });
        executionThread.start();
    }

    private void listenTasks() {
        synchronized (lock) {
            while (true) {
                try {
                    if(executionQueue.isEmpty()) {
                        lock.wait();
                    }
                    Runnable runnable = executionQueue.poll();
                    runnable.run();
                } catch (InterruptedException ie) {
                    ie.printStackTrace();
                }
            }
        }
    }
} 

这是 RunnableWithCompl class :

public class RunnableWithCompl implements Runnable {

    private CompletionHandler completionHandler;
    private Runnable runnable;

    public RunnableWithCompl(Runnable runnable, CompletionHandler completionHandler) {
        this.runnable = runnable;
        this.completionHandler = completionHandler;
    }

    @Override
    public void run() {
        runnable.run();
        if(completionHandler != null)
            completionHandler.onFinish();
    }
} 

和 CompletionHandler 接口:

public interface CompletionHandler {
    void onFinish();
} 

场景。假设您有一个带有微调器的 Activity(显示 UI 未被阻止)和一个触发长任务的按钮。

private TaskManager taskManager;

public void init() {
    taskManager = new TaskManager();
    launchLongTask();
}

private void onButtonClick() {
      launchLongTask() ;
}

private void launchLongTask() {
    Runnable longTask = new Runnable() {
            @Override
            public void run() {
                try {
                    Thread.sleep(15000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        };

        Log.d(TAG, "Launching long task");

        taskManager.executeAsyncWithCompl(longTask, new CompletionHandler() {
            @Override
            public void onFinish() {
                Log.d(TAG, "Long task finished");
            }
        });
} 

问题出在您的 startListening() 实施中。 它在执行任务时将监视器保持在 lock,这意味着在它工作时没有其他方法可以获取监视器。 这意味着 release()executeRunnable(...) 将阻塞,直到没有更多的可运行对象排队。

这也意味着如果线程 运行 startListening() 在其他线程之前得到通知,线程可能会阻塞,因为这意味着这些线程在释放监视器之前无法继续。