简体   繁体   English

同步,锁定和等待阻止主UI线程

[英]Synchronized, lock and wait blocking main UI thread

I have made a simple TaskManager trying to manage a Runnable queue that is needed for my project. 我做了一个简单的TaskManager试图管理我的项目所需的Runnable队列。 However, with a simple scenario, adding a new Runnable blocks the calling thread (main UI thread). 但是,在一个简单的场景中,添加新的Runnable阻止调用线程(主UI线程)。

It happens when you add a new task while a current task is not finished. 当您在当前任务尚未完成时添加新任务时,就会发生这种情况。 You can find below a scenario that reproduces it. 您可以在下面找到再现它的方案。 I don't clearly understand why, and how I could prevent this. 我不清楚原因为何,以及如何避免这种情况。

This is the task manager 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();
                }
            }
        }
    }
} 

Here is the RunnableWithCompl class : 这是RunnableWithCompl类:

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();
    }
} 

And the CompletionHandler interface : 和CompletionHandler接口:

public interface CompletionHandler {
    void onFinish();
} 

The scenario. 该方案。 Let's say you have an Activity with a spinner (to show UI is not blocked), and a button to trigger long tasks. 假设您有一个带有微调器的“活动”(以显示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");
            }
        });
} 

The problem is in your startListening() implementation. 问题出在您的startListening()实现中。 It holds the monitor to lock while it is executing tasks which means no other method can obtain the monitor while it is doing work. 它使监视器在执行任务时lock ,这意味着在工作时没有其他方法可以获取监视器。 This means release() and executeRunnable(...) will block until there are no more runnables queued. 这意味着release()executeRunnable(...)将阻塞,直到没有更多可运行的对象排队。

It also means the thread might block if the thread running startListening() is notified before other threads, because it means those threads cannot continue until it releases the monitor. 这也意味着,如果在其他线程之前通知运行startListening()线程,则该线程可能会阻塞,因为这意味着这些线程在释放监视器之前无法继续。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM