同步、锁定和等待阻塞主线程 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()
在其他线程之前得到通知,线程可能会阻塞,因为这意味着这些线程在释放监视器之前无法继续。
我做了一个简单的 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()
在其他线程之前得到通知,线程可能会阻塞,因为这意味着这些线程在释放监视器之前无法继续。