锁顾名思义就是对某些东西的安全保护。而Java中的锁则是用于处理多线程环境中的共享资源的一种安全机制,主要用于保证线程执行的原子性,顺序性和可见性。
锁可以分为两大类,共享锁和排他锁。共享锁又叫做读锁,是数据读取状态下的一种锁,这种情况下没有数据修改,也就不存在线程安全的问题,即也可以叫做无锁;排他锁又叫做写锁,是数据修改状态下的一种锁,分为读写和写写两种场景,即是我们常需要处理的场景锁。
任何事物都有两面性,锁也不例外,在保护线程安全的同时却牺牲了程序运行的性能,为了平衡程序安全与性能,也就有了下面我要给大家介绍的各种锁机制。
1.锁的粒度
Java代码中我们可以选择给某个类加锁,某个方法加锁,或者某段代码加锁。锁的粒度越小,程序运行的性能自然也就越高,这个需要我们程序员自己把控。
2.乐观锁
也叫无锁,及没有加锁的一种处理方式,实现方式CAS,推荐读多写少的场景下使用。
3.悲观锁
悲观锁与乐观锁相对立,是显示声明的一种加锁方式,例如Java中常用的synchronized,Lock,ReentrantLock等。说到悲观锁又不得不提偏向锁,轻量级锁和重量级锁三个概念,这是jvm内部对于锁的一种优化。
(1)偏向锁
当一个锁一直只有一个线程占有使用的时候,jvm内部会将锁标记为轻量级锁,此时程序执行的效率和无锁状态相差不大。
(2)轻量级锁
对于一个偏向锁,若存在多个线程抢锁的情况,锁会升级为轻量级锁,轻量级锁会损耗一定的性能,但也还好。
(3)重量级锁
轻量级锁中还有一个锁自旋的概念,即当线程没有抢到锁时会进入自旋重新获取锁的操作,当一个轻量级锁达到一定次数的自旋后会升级为重量级锁(默认10)。重量级锁会将抢占锁的线程从用户态转换为内核态,开销很大,非常消耗性能。