Java 中的各种锁

公平锁/非公平锁

当前锁被释放时,AQS 中的线程们获取锁的规则。
公平锁是指,线程按照先来后到顺序获取锁。
非公平锁指,锁刚释放时,刚好有线程要获取锁,就不用排队,锁直接给它。

可重入锁

线程在外层方法获取锁的时候,在进入内层方法会自动获取该锁。一般都是可重入锁。

独享锁/共享锁

独享锁:一个锁只能被一个线程持有。 共享锁:一个锁可以被多个线程持有。这种适用于大量的线程同时读的情况。

互斥锁/读写锁

互斥锁/读写锁是独享锁/共享锁的具体实现。可以认为:
互斥锁是一种独享锁。
读写锁是一种共享锁。

乐观锁/悲观锁

锁的存在就是因为并发情况下修改数据,会造成各种问题。乐观和悲观是指:是否认为拿到数据之后是否一定会修改数据。
乐观锁:大多数情况下,线程或者事务拿到数据都是读,很少会写。所以你可以放心把锁共享出去。给数据一个版本号,版本冲突了,就证明同时写了,再解决。
悲观锁:线程或者事务拿到数据肯定会修改的,所以不要给它机会共享,一定要用独享锁。

分段锁

它不是锁的种类,只是锁优化的一种方式。它把数据分段加锁,这样减小冲突的概率。
比如 ConcurrentHashMap ,它将所有的 key 分成若干段,每段单独加锁,不同的段是可以同时 put/get 的,大大提高并发性能。

自旋锁

它不是锁的种类,只是获取锁的一种方式。
它认为,大多数情况下,线程占用锁只是执行一小段代码,因此发现锁被别人拿走了之后,没必要垂头丧气地休眠,而是做做热身(执行几个无聊的循环)再获取几次,待会儿锁就被还回来了。

偏向锁/轻量级锁/重量级锁

这三个是指 synchronized 中锁的状态。

偏向锁

偏向锁认为,绝大多数情况下,多个线程都不会争抢一个锁。
对象头中标记一下当前获取锁的线程号即可,等下次有线程过来拿锁,检查线程号是否相同,相同则立即放行;不同就转入轻量级锁。

轻量级锁

当前线程在获取锁之前,在自己的栈里记下锁对象的对象头,然后试着使用 CAS 将锁对象头中的 MarkWord 字段也指向自己锁记录的地址。如果获取成功,就拿到锁。获取失败,就自旋几次,尝试获取。还获取不到,就阻塞,进入重量锁。
解锁时仍然是执行 CAS 替换,替换成功则释放成功。替换失败,说明有别人尝试获取该锁,则需要在释放锁的同时唤醒被挂起的线程。

重量级锁

操作系统层面的锁,即没有拿到锁的线程进入阻塞状态,等待持有锁的线程执行完之后唤醒它

Search

    欢迎关注我的微信公众号

    Bishion

    Table of Contents