ReentrantLock
与 synchronized
都是悲观锁 ;
ReentrantLock
是 Lock
接口的实现类 ,
public class ReentrantLock implements Lock, java.io.Serializable {
}
Lock
是一种锁的机制 , 调用 lock()
方法 , 表示要对下方的代码进行加锁 , 这些代码是线程安全的 ;
代码执行完毕后 , 调用 unlock()
释放锁 ;
在 lock()
与 unlock()
之间的内容 , 就是同步代码块内容 ;
public interface Lock {
void lock();
void lockInterruptibly() throws InterruptedException;
boolean tryLock();
boolean tryLock(long time, TimeUnit unit) throws InterruptedException;
void unlock();
Condition newCondition();
}
悲观锁都是重量级锁 ;
JDK
之间 , 只有一个 synchronized
重量级锁 ;
Java 虚拟机创建了线程 A , B 两个线程 , JVM 将线程托管给操作系统进行调度执行 , 线程同步依靠 synchronized
重量级锁实现 , 线程 A , B 之间会进行竞争 , 哪个抢到 synchronized
锁 , 哪个线程就可以执行 ;
使用 synchronized
使用起来效率很低 , 假如在 synchronized
同步代码块中 , 只有一行代码 , 执行
ms , 但是系统调度线程 , 可能需要
ms 才能轮到线程执行 , 线程执行的时间远远小于调度时间 , 这样线程执行效率很低 ;
为了 Java 程序的提升执行效率 , Java 引入了
种锁状态 , 无锁 , 偏向锁 , 轻量级锁 , 重量级锁 ;
Java 虚拟机堆内存中的对象数据中 , 每个对象都有一个对象头 , 结构如下 :
对象头 中封装了 锁的状态 , 当锁的状态发生改变时 , 对应的锁的标志位也进行相应修改 ;
无锁状态 : 不进行加锁 , 线程不安全 ;
偏向锁 : 第
个访问 共享资源 的线程 A , 做一个标记 , 不加锁 , 这个标记称为 " 偏向锁 " ; 偏向锁 偏向第一个访问的线程 ; 如果没有新的线程竞争该锁 , 则该 偏向锁一直被该线程持有 , 不会释放锁 ; 如果出现多个线程同时访问 , 持有偏向锁的线程会 释放该偏向锁 , 并添加轻量级锁 ;
轻量级锁 : 自旋锁 , 等待期间一直做自旋操作 , 效率较高 , 但是空耗 CPU 性能 ; 自旋就是 while / for 循环 ;
重量级锁 : 系统提供的 synchronized
, ReentrantLock
等重量级锁 , 由操作系统进行调度 , 可进行阻塞 ;
锁的四种状态之间转换 : 在保证线程安全的前提下 , 尽可能提升效率 ;
个线程访问共享资源时 , 无锁状态升级为偏向锁 ;
个线程再来访问 共享资源 时 , 偏向锁 升级为 轻量级锁 ;
次自旋 , 自动将锁升级为重量级锁 ;