简体   繁体   中英

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. However, with a simple scenario, adding a new Runnable blocks the calling thread (main UI thread).

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 :

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 :

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.

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. 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. This means release() and executeRunnable(...) will block until there are no more runnables queued.

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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