tryLock()在循环中?

时间:2017-09-25 02:38:53

标签: java multithreading concurrency

tryLock()无法获得锁定。因此,如果我们使用返回值来执行工作,我们可能根本无法完成工作。

Lock lock = new ReentrantLock();
boolean isLocked = lock.tryLock();

if (isLocked) {
    try {
        doWork();
    } finally {
        lock.unlock();
    }
}

synchronized会阻止,直到获得锁定为止,因此我们知道doWork()最终会完成。

那么,在获取锁之前我们应该tryLock()在循环内是否正确?

boolean isLocked = false;

while (!isLocked) {
    isLocked = lock.tryLock();
    Thread.sleep(100);
}

if (isLocked) {
    try {
        doWork();
    } finally {
        lock.unlock();
    }
}

2 个答案:

答案 0 :(得分:4)

几乎从不,不。

如果您需要获取锁定,只需使用lock()电话即可。这将立即获得它,如果可用,或等待,如果没有。在tryLock()上重复循环也有等待锁的效果,如果它不可用,但没有允许适当的OS控制的阻塞和等待线程的唤醒。相反,等待线程将继续占用CPU,燃烧CPU时间和电源,同时等待锁定被释放。

在最糟糕的情况之一 2 中,拥有锁的线程可以被上下文切换出来并等待可用的CPU继续,但是CPU不可用,因为它被正在使用一个线程在tryLock上忙着循环。它几乎是临时死锁的形式 - 暂时的,因为最终tryLock线程会使用它的量子并被安排关闭,但整个过程可能比使用{{1的通常方法要长许多个数量级}}

所有这一切都说lock()的用途非常有限。如果您在紧密循环中使用tryLock(),那么您几乎肯定会做错事。最好在您不

一个例子可能是你必须对几个独立的对象执行相同的操作,每个对象都有自己的锁。默认方法只是依次为tryLock()lock()每个对象。一种可能更快的方法是首先unlock()每个对象,更新你获得锁定的任何对象(也许你不止一次),然后返回tryLock()你错过的任何对象第一次。通过最初不等待任何锁定,并继续使用其余对象,您可以在竞争系统中实现更高的更新速率。

lock()可能有用的另一种情况是,您可以通过多种方式获得相同的效果。想象一下例如某种"分布式计数器" 1 ,其中你有一个逻辑计数器,它被实现为N个独立的等效计数器,每个计数器都有自己的锁。逻辑计数器的只是N个底层计数器的总和。要递增计数器(例如),您只需要递增N个计数器中的任何一个。在这种情况下,有人试图更新计数器只能tryLock()基础计数器,直到找到一个可用的计数器"并修改它。这可能比具有单个锁的单个计数器更好地扩展。

1 当然,对于一个简单的计数器,您可能会使用原子整数,例如tryLock()或更好,AtomicInteger - 但是对于更复杂的结构,这可能是现实的。

2 如果LongAdder线程的优先级高于拥有锁的线程,则会出现更糟糕的情况,并且调度策略严格处理优先级(即优先级较低的线程永远优先于tryLock()SCHED_FIFO优先获得优先权。在这种情况下,优先级较高的线程可以在SCHED_RR上无限期地 ,因为拥有它的低优先级线程将永远不会因为存在可运行的更高优先级线程而被唤醒。所以你甚至可以陷入僵局。

答案 1 :(得分:0)

tryLock()用于一个线程,当它无法获取锁时,可以执行某些其他有用的工作:

while (there_still_is_work_to_be_done) {
    if (lock.tryLock()) {
        try {
            doSomeWorkThatNeedsTheLockToBeLocked();
        finally {
            lock.unlock();
        }
    }

    doSomeOtherUsefulWork();
    waitABit();
}