常见的并发锁
2018-09-02 本文已影响891人
游牧族人
乐观锁
乐观锁总是认为数据是安全的,当我们获取数据的时候总是认为不会有其他人去尝试修改数据信息,因此获取数据时不会上锁,但是在对数据更新时会去判断原数据是否已经被修改。若没有被修改则更新当前数据,否则重新尝试。乐观锁一般使用添加 version 字段或 CAS 方式实现。
悲观锁
悲观锁总是认为数据是不安全的,但我们获取数据的时候总是认为会有其他人尝试修改数据而导致数据不一致,因此在获取数据时会加锁,其他线程无法对共享数据进行操作,直到锁线程操作结束。java中的synchroinzed关键字就是一种悲观锁。
乐观锁与悲观锁属于抽象锁,他定义了锁的实现原理,但并不是具体的实现锁。
自旋锁
自旋锁是一种保护多线程共享资源的锁类型,自旋锁在尝试获取锁时若竞争失败则不会使线程挂起而是循环继续尝试获取锁。由于自旋锁不会阻塞线程,因此线程会持续消耗CPU资源,若是长时间没有获取锁,则是对CPU资源的一种浪费。
自旋锁适用于线程锁竞争不激烈同时线程占用锁的时间不长的多线程环境下
互斥锁
互斥锁是一次最多只能有一个线程持有的锁。当一个线程持有互斥锁后,其他线程便进入阻塞状态,直到锁线程执行完同步代码块释放同步锁其他线程才有机会继续竞争锁。
互斥锁适用于线程竞争激烈且线程占用锁时间比较长的多线程环境下
重入锁
一种对于同一个线程可以多次进入的互斥锁。每进入一次线程数量加一,退出一次线程数量减一,直到锁线程数量为零时释放当前锁。java 中重入锁的实现为 ReentrantLock 。
JVM锁
Java对象头
![](https://img.haomeiwen.com/i5344041/37dd74547c28e925.png)
![](https://img.haomeiwen.com/i5344041/7cc24fbcdfe807fb.png)
锁分类
锁状态级别:无锁状态---偏向锁---轻量级锁---重量级锁
这四个锁状态会随着线程之间的竞争激烈程度进行升级,其中锁状态只能升级而不能降级。这是为了提高获取锁和释放锁效率。
-
偏向锁
研究发现,大多数情况下,锁不存在多线程竞争,而且总是同一个线程获得。为了让线程获得锁的代价更低引入了偏向锁,而这个经常获取锁的线程就称之为锁的偏向线程。
偏向锁的获取:
当一个线程第一次进入同步代码块时,会使用 CAS 方式更新锁对象的 Mark Word 区域,将该线程的线程 id 记录下来同时更新偏向锁标志位为 1,表示该线程已经持有偏向锁。线程退出同步代码块时会保留锁对象的头部信息;当线程再次访问同步代码块时,会检查锁对象中记录偏向线程的 id 值是否和当前线程 id 值相同,若相同则表示当前线程持有了偏向锁,直接进入同步代码块;若不同则使用CAS竞争锁,竞争成功可以重新获取偏向锁,竞争失败则将偏向锁升级为轻量级锁。
偏向锁的释放:
偏向锁只有在其他线程尝试竞争偏向锁时,持有偏向锁的线程才会释放偏向锁,除此之外线程不会主动去释放偏向锁。释放偏向锁时需要等待全局安全点(当前时间没有字节码在运行),暂停当前线程同时会检测当前线程是否持有偏向锁,若持有的话则撤销偏向锁,锁状态恢复到无锁状态或升级为轻量级锁状态。 -
轻量级锁
轻量级锁使用CAS修改对象的mark word区域为当前线程信息,若修改成功则当前线程获得轻量级锁,否则的话尝试使用自旋来获取锁。若是有两个或以上线程同时竞争该锁,则轻量级锁会膨胀为重量级锁。 -
重量级锁
当其他线程试图竞争锁时,都将会进入阻塞状态。只有当前用有锁的线程释放锁之后才唤醒其他线程继续竞争。
偏向锁,轻量级锁都属于乐观锁,重量级锁属于悲观锁