1.Synchronized锁
底层是monitor
监视器,每一个对象再创立的时候都会常见一个monitor
监视器,在应用synchronized
代码块的时候,会在代码块的前后产生一个monitorEnter和monitorexit
指令,来标识这是一个同步代码块。
1.1 执行流程
线程遇到同步代码块,给这个对象monitor
对象加1
,当线程退出以后代码块当前,给这个对象的monitor
对象减一,如果monitor
指令的值为0
则以后线程开释锁。
1.2 反编译源码
同步代码块反编译
public void test01(){ synchronized (this){ int num = 1 ; } }
两次monitorexit
的作用是防止同步代码块无奈跳出,因而存在两种,失常退出和异样退出
同步办法反编译
public synchronized void test01(){ int num = 1 ; }
能够发现其没有在同步办法前后增加monitor
指令,然而在其底层实际上也是通过monitor
指令实现的,只不过相较于同步代码块来说,他是隐式的。
1.3 锁降级
在JDK1.5
的时候对于synchronzied
做了一系列优化操作,减少了诸如:偏差锁,轻量级锁,自旋锁,锁粗化,重量级锁的概念。
1.3.1 偏差锁
在一个线程在执行获取锁的时候,以后线程会在monitor
对象中存储指向该线程的ID。当线程再次进入的时候,不须要通过CAS的办法再来进行加锁或者解锁,而是检测偏差锁的ID是不是以后要进行的线程,如果是,间接进入。
偏差锁,实用于一个线程执行工作的状况
在JDK1.6
中,默认是开启的。能够通过-XX:-UseBiasedLocking=false
参数敞开偏差锁
1.3.2 轻量级锁
轻量级锁是指锁为偏差锁的时候,该锁被其余线程尝试获取,此时偏差锁降级为轻量级锁,其余线程会通过自旋的形式尝试获取锁,线程不会阻塞,从而提供性能
降级为轻量级锁的状况有两种:
- 敞开偏差锁
- 有多个线程竞争偏差锁的时候
具体实现:
线程进行代码块当前,如果同步对象锁状态为无锁的状态,虚拟机将首先在以后线程的栈帧中创立一个锁记录的空间。这个空间内存储了以后获取锁的对象。
应用状况:
两个线程的相互拜访
1.3.3 重量级锁
在有超过2个线程拜访同一把锁的时候,锁主动降级为重量级锁,也就是传统的synchronized
,此时其余未获取锁的线程会陷入期待状态,不可被中断。
因为依赖于monitor
指令,所以其耗费系统资源比拟大
下面的三个阶段就是锁降级的过程
1.3.4 锁粗化
当在一个循环中,咱们屡次应用对同一个代码进行加锁,这个时候,JVM会主动实现锁粗化,即在循环外进行增加同步代码块。
代码案例:
锁粗化之前:
for (int i = 0; i < 10; i++) { synchronized (LockBigDemo.class){ System.out.println(); } }
锁粗化之后:
synchronized (LockBigDemo.class){ for (int i = 0; i < 10; i++) { System.out.println(); } }
本次对于synchronized
的底层原理没有以代码的形式开展,之后笔者会出一篇synchronized
底层原理分析的文章
2. Lock锁
一个类级别的锁,须要手动开释锁。能够选择性的抉择设置为偏心锁或者不偏心锁。期待线程能够被打断。
底层是基于AQS
+AOS
。AQS
类实现具体的加锁逻辑,AOS
保留获取锁的线程信息
2.1 ReentrantLock
咱们以ReentrantLock
为例解析一下其加锁的过程。
2.1.1 lock办法
首先通过ReentrantLock
的构造方法的布尔值判断创立的锁是偏心锁还是非偏心锁。
假如当初创立的是非偏心锁,他首先会判断锁有没有被获取,如果没有被获取,则间接获取锁;
如果锁曾经被获取,执行一次自旋,尝试获取锁。
如果锁曾经被获取,则将以后线程封装为AQS
队列的一个节点,而后判断以后节点的前驱节点是不是HEAD
节点,如果是,尝试获取锁;如果不是。则寻找一个平安点(线程状态位SIGNAL=-1
的节点)。
开始一直自旋。判断前节点是不是HEAD
节点,如果是获取锁,如果不是挂起。
源码解读:
- 非偏心锁
lock
final void lock() { //判断是否存在锁 if (compareAndSetState(0, 1)) //获取锁 setExclusiveOwnerThread(Thread.currentThread()); else acquire(1); }
public final void acquire(int arg) { if (!tryAcquire(arg) && acquireQueued(addWaiter(Node.EXCLUSIVE), arg)) selfInterrupt(); }
//非偏心锁的自旋逻辑protected final boolean tryAcquire(int acquires) { return nonfairTryAcquire(acquires); }final boolean nonfairTryAcquire(int acquires) { final Thread current = Thread.currentThread(); //获取锁状态 int c = getState(); //如果锁没被获取,获取锁 if (c == 0) { if (compareAndSetState(0, acquires)) { setExclusiveOwnerThread(current); return true; } } //以后线程曾经获取到了锁 else if (current == getExclusiveOwnerThread()) { //线程进入次数减少 int nextc = c + acquires; if (nextc < 0) // overflow throw new Error("Maximum lock count exceeded"); setState(nextc); return true; } return false; }
//将线程封装为一个线程节点,传入锁模式,排他或者共享private Node addWaiter(Node mode) { Node node = new Node(Thread.currentThread(), mode); // 获取尾节点 Node pred = tail; //如果尾节点不为Null,间接将这个线程节点增加到队尾 if (pred != null) { node.prev = pred; if (compareAndSetTail(pred, node)) { pred.next = node; return node; } } //为空,自旋设置尾节点 enq(node); return node; }private Node enq(final Node node) { for (;;) { Node t = tail; //初始化 if (t == null) { // Must initialize if (compareAndSetHead(new Node())) tail = head; } else { node.prev = t; //将头结点和尾结点都设置为以后节点 if (compareAndSetTail(t, node)) { t.next = node; return t; } } } }
//尝试入队final boolean acquireQueued(final Node node, int arg) { boolean failed = true; try { boolean interrupted = false; for (;;) { //获取节点的前驱节点,如果前驱节点为head节点,则尝试获取锁 final Node p = node.predecessor(); if (p == head && tryAcquire(arg)) { setHead(node); p.next = null; // help GC failed = false; return interrupted; } //如果不是,寻找平安位 if (shouldParkAfterFailedAcquire(p, node) && parkAndCheckInterrupt()) interrupted = true; } } finally { if (failed) cancelAcquire(node); } }
private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) { int ws = pred.waitStatus; //前驱节点曾经平安 if (ws == Node.SIGNAL) return true; //前驱节点不平安,寻找一个线程状态为`Signal`的节点作为前驱节点 if (ws > 0) { do { node.prev = pred = pred.prev; } while (pred.waitStatus > 0); pred.next = node; } else { //否则间接设置这个前驱节点的线程期待状态值 compareAndSetWaitStatus(pred, ws, Node.SIGNAL); } return false; }//中断线程private final boolean parkAndCheckInterrupt() { LockSupport.park(this); return Thread.interrupted(); }
2.1.2 unlock办法
代码解读:
public void unlock() { sync.release(1); }
public final boolean release(int arg) { //尝试开释锁 if (tryRelease(arg)) { //获取队列头元素,唤醒该线程节点,执行工作 Node h = head; if (h != null && h.waitStatus != 0) unparkSuccessor(h); return true; } return false; }
protected final boolean tryRelease(int releases) { int c = getState() - releases; //判断是否为以后线程领有锁 if (Thread.currentThread() != getExclusiveOwnerThread()) throw new IllegalMonitorStateException(); boolean free = false; //开释胜利 if (c == 0) { free = true; setExclusiveOwnerThread(null); } setState(c); return free; }
private void unparkSuccessor(Node node) { int ws = node.waitStatus; if (ws < 0) compareAndSetWaitStatus(node, ws, 0); Node s = node.next; if (s == null || s.waitStatus > 0) { s = null; for (Node t = tail; t != null && t != node; t = t.prev) if (t.waitStatus <= 0) s = t; } //唤醒下一个节点 if (s != null) LockSupport.unpark(s.thread); }
2.1.3 Node节点
/** 共享锁,读锁应用 */ static final Node SHARED = new Node(); /** 独占锁*/ static final Node EXCLUSIVE = null; /** 不平安线程 */ static final int CANCELLED = 1; /** 须要进行线程唤醒的线程 */ static final int SIGNAL = -1; /**condition期待中 */ static final int CONDITION = -2; //线程期待状态 volatile int waitStatus; volatile Node prev; volatile Node next; volatile Thread thread; Node nextWaiter;
3. Lock锁和Synchronized的区别
Lock
锁是API层面,synchronized
是CPU
源语级别的Lock
锁期待线程能够被中断,synchronized
期待线程不能够被中断Lock
锁能够指定偏心锁和非偏心锁,synchronized
只能为非偏心锁Lock
锁须要被动开释锁,synchronized
执行完代码块当前主动开释锁
更多原创文章请关注公众号@MakerStack