等待/通知vs睡眠/中断vs ReentrantLock。条件

时间:2018-09-28 12:56:28

标签: java android multithreading thread-sleep reentrantlock

我正在编写一种“按类型搜索”机制(android),该机制在后台线程中进行sqlite查询,并将结果发布回UI线程。理想情况下,线程应等待/休眠,唤醒以执行任何接收到的Runnable对象,然后返回休眠状态。最好的方法是什么?为什么?

基本上,我想了解这3个选项之间的主要区别是什么,哪种是最适合这种情况的

  1. 睡眠/中断

    public class AsyncExecutorSleepInterrupt {
    private static Thread thread;
    private static Runnable runnable;
    
    static {
        thread = new Thread(() -> {
            while (true) {
                try {
                    Thread.sleep(10000);
                } catch (InterruptedException e) {
                    if (runnable != null) {
                        runnable.run();
                        runnable = null;
                    }
                }
            }
        });
        thread.start();
    }
    
    public static void execute(Runnable runnable) {
        AsyncExecutorSleepInterrupt.runnable = runnable;
        thread.interrupt();
    }}
    
  2. 等待/通知

    public class AsyncExecutorWaitNotify {
    private static Thread thread;
    private static Runnable runnable;
    
    private static final Object monitor = new Object();
    
    static {
        thread = new Thread(() -> {
            while (true) {
                synchronized (monitor) {
                    try {
                        monitor.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                        continue;
                    }
                    if (runnable != null) {
                        runnable.run();
                        runnable = null;
                    }
                }
            }
        });
        thread.start();
    }
    
    public static void execute(Runnable runnable) {
        AsyncExecutorWaitNotify.runnable = runnable;
        synchronized (monitor) {
            monitor.notify();
        }
    }}
    
  3. ReentrantLock

    public class AsyncExecutorLockCondition {
    
    private static final ReentrantLock lock = new ReentrantLock();
    
    private static final Condition cond = lock.newCondition();
    
    private static Thread thread;
    
    private static Runnable runnable;
    
    static {
        thread = new Thread(() -> {
            while(true){
                try {
                    lock.lock();
                    cond.await();
                    runnable.run();
                    lock.unlock();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        thread.start();
    }
    
    public static void execute(Runnable runnable) {
        AsyncExecutorLockCondition.runnable = runnable;
        lock.lock();
        cond.signal();
        lock.unlock();
    }}
    

2 个答案:

答案 0 :(得分:1)

以上都不是。第三是最接近的方法,但仍然不是最佳方法。使用looper + handler或消息队列。另外,应该有一条消息可以发送给任何线程,告诉它退出循环并终止。否则,当不再需要它时,它将泄漏它可以引用的所有内存(这将是很多事情,因为它将对其父对象进行内部引用),但是无论如何它都会永久存在。请记住,线程直到退出之前都不会被GC。

答案 1 :(得分:1)

我个人不喜欢第一种方法,可能主要是因为interrupt。如果有人以某种方式呼叫并中断该线程怎么办?您将运行一些任意代码,可能不是最好的主意。另外,当您中断时,实际上是在用异常链填充堆栈跟踪,这是抛出异常最昂贵的部分。

但是假设您不在乎第二点,而您完全可以控制第一点; IMO的这种方法可能没有什么问题。

现在,在此示例中,Conditionalwait/notify之间的差异很小。我不知道内部细节,哪些细节可能更快或更更好,但通常首选Conditional;主要是因为它更容易阅读,至少对我而言。与Conditional不同,synchronized可以始终从不同锁中获取。

其他优点是(此处无关):您可以创建多个条件,从而仅唤醒所需的线程;不像notifyAll。然后有一些到期的方法,例如awaitUntil(Date)await(long, TimeUnit)awaitNanos。甚至有一种方法将await忽略interruptsawaitUninterruptibly

话虽这么说,您不需要在lock::unlock之后输入await,因为有关此方面的文档非常清楚:

  

与此条件相关联的锁已自动释放...

一种更直接的方法是:

static class AsyncExecutor {

    private static final ExecutorService service = Executors.newSingleThreadExecutor();

    public static void execute(Runnable runnable) {
        service.execute(runnable);
    }
}