锁
还记得前两年出去面试的时候,面试官总是会问你使用过哪些锁?哪些场景下使用哪些锁等等问题?
首先Java中最经常使用到的synchronized就是一种互斥锁,根据java内存模型happen-before规则又可以保证线程间的同步。所以说管程是解决并发问题的万能钥匙。
ReentrantLock是互斥的可重入锁。同一个线程可以lock多次,并且支持公平锁和非公平锁。
ReadWriteLock读写锁。主要针对读多写少的场景。允许多个线程同时读共享变量;只允许一个线程写共享变量;如果一个写线程正在执行写操作,此时禁止读线程读共享变量。读写锁的写操作是互斥的,当一个线程在写共享变量的时候,是不允许其他线程执行写操作和读操作。
锁的升级:先获取读锁,并且释放之前,读锁再升级为写锁。但是ReadWriteLock并不支持这种升级,读锁还没有释放,此时获取写锁,会导致写锁永久等待,最终导致相关线程都被阻塞,永远没有机会被唤醒。所以锁的升级时不允许的。
但是锁的降级是允许的,写锁还没有释放,获取读锁,然后释放写锁,最后再释放读锁,这种场景ReadWriteLock是支持的。
StampedLock锁支持三种锁模式:写锁,悲观读锁和乐观读。
在读多写少的场景,StampedLock的性能比ReadWriteLock锁还要好。
其中写锁和悲观读锁的语义和ReadWriteLock的写锁,读锁的语义非常类似,允许多个线程同时获取悲观锁,但是只允许一个线程获取写锁,写锁和悲观读锁是互斥的。不同的是,StampedLock里的写锁和悲观读锁加锁成功之后,都会返回一个stamp。
StampedLock的性能之所以比ReadWriteLock还要好,其关键是StampedLock支持乐观读的方式。ReadWriteLock支持多个线程同时读,但是当多个线程同时读的时候,所有的写操作会被阻塞;而StampedLock提供的乐观读,是允许一个线程获取写锁的,也就是说乐观读这个操作是无锁的。