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

时间:2016-07-26 11:33:39

标签: java multithreading locking synchronized

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

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

这是任务管理器类:

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类:

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

1 个答案:

答案 0 :(得分:1)

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

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