synchronized做为内置锁,使用简单,不易出错,然鹅确有至关的局限性,例如,没法从等待获取锁的阻塞中中断,没法设置获取锁的超时。
因此JUC提供了另外一种更灵活的加锁方式,即Lock。java
Lock接口定义以下git
public interface Lock { void lock(); void lockInterruptibly() throws InterruptedException; boolean tryLock(); boolean tryLock(long timeout, TimeUnit unit) throws InterruptedException; void unlock(); Condition newCondition(); }
从接口的定义不难发现,Lock不只提供了常规的lock()阻塞式加锁,也提供了tryLock使得线程能在获取不到锁时,立刻返回,
甚至能够等待锁一段时间后,再返回。lockInterruptibly则提供了可中断的阻塞式获取锁方式。github
Lock的锁须要显示释放,一般要与try...finally
语句一块儿使用,避免死锁。ide
lock.lock(); try { // update object state // catch exceptions and restore invariants if necessary } finally { lock.unlock(); }
Lock最经常使用的实现类是ReentrantLock,这是一个可重入锁(synchronized也是)。性能
ReentrantLock默认和内置锁同样,是非公平锁,可是支持公平锁模式,能够用ReentrantLock(true)
建立公平锁。线程
所谓可重入锁,也就是说一个线程能够在持有该锁的时候,再次获取该锁。可重入锁一般与一个计数器关联,第一次获取锁的时候,计数器从0变为1,再次获取锁,变为2,以此类推。释放锁的时候,计数器每次减1,直至减为0,该锁才真正释放给其余线程。
为啥须要可重入锁
举个例子(JCP书上的)scala
public class Widget { public synchronized void doSomething() { ... } } public class LoggingWidget extends Widget { public synchronized void doSomething() { System.out.println(toString() + ": calling doSomething"); super.doSomething(); } }
子类覆盖了父类方法,并再次调用了父类的同步方法,若是锁不支持重入,则会致使死锁。rest
所谓公平锁,其实就是指锁的等待队列执行先进先出,等待最久的线程优先得到锁。
可是内置锁和ReentrantLock默认都是非公平的,为啥?
由于非公平锁的性能更好。一个事实是,一个线程从被唤醒到真正运行中间有不可忽视的延迟,这个延迟时间极可能长到足够一个运行中的线程获取锁,并完成操做,而后释放锁。也便是说,把锁给’等待最久的线程‘的过程当中,可让其余线程插队获取锁,并归还锁,还不会影响’等待最久的线程‘的运行。这样一来吞吐量就获得了提高。code
package io.github.liam8.con import java.util.concurrent.TimeUnit import java.util.concurrent.locks.{Lock, ReentrantLock} object LockDemo { private val rtl: Lock = new ReentrantLock() var inc: Int = 0 def get(): Int = { rtl.lock() try { inc } finally { rtl.unlock() } } def addOne(): Unit = { rtl.lock() try { TimeUnit.SECONDS.sleep(1) inc = 1 + get() } finally { rtl.unlock() } } def main(args: Array[String]): Unit = { for (i <- 1 to 10) { new Thread { override def run(): Unit = { println(s"run thread $i") addOne() } }.start() } while (true) { println(s"inc=$inc") TimeUnit.SECONDS.sleep(1) } } }
outputblog
run thread 3 run thread 8 run thread 1 run thread 9 run thread 7 run thread 4 run thread 5 run thread 2 run thread 10 run thread 6 inc=0 inc=0 inc=2 inc=3 inc=4 inc=5 inc=6 inc=7 inc=8 inc=8 inc=10 inc=10 inc=10
转载请注明原文地址:https://liam-blog.ml/2019/07/21/Scala-Concurrency-in-Practice-2/