学了几天 python,辣条君始终不忘本人其实是个 Javaer。来,跟着辣条君一起看看 Java 锁是如何分类的吧。
Java 锁是为了保障并发过程中,数据保护的准确性。
乐观锁与乐观锁
乐观锁
: 认为以后读取数据的时候,不会有线程去批改数据,所以不须要加锁。当更新数据的时候,首先查看数据和本人已经记录的数据是否统一,如果统一,则更新之;如果不统一,采取一些伎俩,比方报错或者自旋(自旋前面会讲)。
举个例子,一个线程 A 读取账户余额时,不会加锁,读到 20 元,线程 A 账户记录更新为 20 元。而后线程 A 为账户余额减少 5 元,当初想把账户余额更新为 25 元,先去查看账户当初的数值为 20 元,和账户记录统一,就将账户余额间接更新为 25 元,同时将本人的账户记录更新为 25 元。过了一会儿,线程 A 又想给账户余额减少 5 元,于是拿着 30 元去更新账户余额。此时,发现账户余额为 100 元,和本人的账户记录 (25 元) 不统一了,就报错(或者自旋),此次更新失败。
CAS
(比拟替换)就是一种常见的乐观锁实现计划,java.util.concurrent.atomic
中的那些原子类就是通过 CAS
算法实现的。为了保障更新的原子性,原子类最终本质上是通过 JNL 调用了 CPU 的原子操作。CAS
先天有两点有余:1、ABA
问题。2、长时间的 自旋
会耗费过多的资源。
所以 乐观锁多用于读数据多的场景,效率较高。
乐观锁
: 认为无论本人进行什么操作,那一瞬间都会有其余线程来净化数据,所以肯定要加锁。
乐观锁很好了解,不须要加什么例子了,synchronized
和 Lock
都属于乐观锁。对于这两个类的应用,我想另写一篇博客,毕竟日常应用比拟多嘛。乐观锁多用于写数据多的场景
自旋锁
上一节重复提到自旋,自旋到底是个什么东东呢?
首先咱们要晓得,一个 Java 线程被阻塞,会放弃 CPU 使用权;被唤醒,会从新取得 CPU 使用权。这两个切换上下文的过程,是极其耗费资源的。如果,一个同步操作 (线程占用锁) 的工夫极短,那须要用锁的线程能够先等一会儿,待会不必进行上下文切换,拿到锁间接执行,那岂不是极好的。这个 期待操作 就叫做自旋。
自旋操作个别会规定自旋次数,如果肯定次数还是没有失去锁,那就放弃自旋,进行阻塞。为了更加晋升效率,自适应自旋锁呈现了,它不拘泥于固定的次数,而是依据以往教训,如果以前自旋一段时间能够失去锁,那么超过最大自旋数的时候,容许多自旋几次;如果以往教训总是失败,那么不肯定非得达到最大自旋数,就间接进入自旋状态。
无锁、偏差锁、轻量级锁、重量级锁
依据切换资源耗费老本,能够将锁分为无锁、偏差锁、轻量级锁、重量级锁。
无锁
: 就是不对资源加锁,例如下面讲到的 CAS 算法,只是在更新的时候进行一下比拟判断就好。
偏差锁
: 有一种现实的状态,一段时间内只有一个线程拜访同步代码块,这样是不是连更新时比拟的步骤都能够省略了。这种状况下能够挂上 偏差锁 ,这样该线程在拜访同步代码块的时候就不须要CAS
操作了。当,有其余线程来访问共享资源的时候,偏差锁主动降级为 轻量级锁。如果没有线程来打搅,只有当虚拟机运行到全局平安点的时候能力撤销偏差锁。
轻量级锁
: 当一个线程领有轻量级锁,另一个线程想领有这把锁,不会进入阻塞状态,而是先自旋,期待取得锁的机会。然而,当多个线程(至多两个) 来获取这把锁时,这把锁会间接降级为 重量级锁。
重量级锁
: 当一个线程领有重量级锁时,其余线程想要获取该锁,都会间接进入阻塞状态。在 JDK1.6 之前synchronized
机制应用的时 重量级锁 ,1.6 版本之后开始应用 轻量级锁 和偏差锁
偏心锁和非偏心锁
偏心锁
: 当多个线程申请获取锁时,依据申请的先后顺序放到一个队列里,而后按程序获取锁。此时,线程从阻塞到唤醒是须要上下文切换的。保障公平性,然而效率可能较低。
非偏心锁
: 非偏心锁,尝试被线程获取的时候,不肯定从线程队列中获取,先看看此时有没有新的线程来获取本锁,如果有,间接把锁给该线程,不须要进行上下文切换。失去公平性,然而可能会提高效率。
可重入锁
可重入锁
是指同一个线程能够屡次加同一把锁。ReentrantLock
和 synchronized
都属于 可重入锁。
public class MyTest {
// 办法嵌套
public synchronized void outThing() {
// do someting
innerThing();}
public synchronized void innerThing() {// do something}
}
看下面这种状况,办法嵌套通过 synchronized
机制 2 次获取了对象的锁(Monitor)。如果是非可重入锁,肯定会产生死锁。
共享锁和独享锁
共享锁
: 一个线程给共享资源加上共享锁后,其余线程还能够给这个共享资源加上其余的共享锁。比方常见的读锁。
独享锁
: 一个资源被加上独享锁后,就不能增加其余锁了。比方常见的写锁。
共享锁和独享锁在 mysql 层面也是通用概念。
小结
有了这些 Java 锁的概念,再去看代码就不便多了。接下来会好好钻研下 synchronized
和ReentrantLock
。