- 1、 synchronized
- 1.1 、 使用方法
- 1.2 、概念
- 1.2.1 、偏向锁
- 1.2.2 、轻量锁
- 1.2.3 、重量锁
- 1.2.4 、重入锁
- 1.2.4 、公平、非公平锁
- 2、 ReentrantLock
- 2.1、 使用方法
- 2.2 、ReentrantLock 源码
- 2.2.1、 AbstractQueuedSynchronizer
- 2.2.2 、非公平锁的实现
- 2.2.3 公平锁的实现
- 2.2.4 锁释放
- 2.2.5 Condition
本文是基于jdk1.8,其他版本的jdk的差异暂不考虑。
1.1 、 使用方法// 锁实例对象或者锁class
void fun(){
synchronized (object) {
// todo
}
synchronized (A.class) {
// todo
}
}
锁方法
void synchronized fun(){
// todo
}
如果是锁对像object,则要求object是全局不可变对象,比如下面代码,虽然锁了全局的count ,但count是Integer 型,每次操作后都是一个新的对象(-128~127 是缓存的同一个对象,见 Integer valueOf(int i)) :
public class IntLockTest {
Integer count = 0;
public static void main(String[] args) throws InterruptedException {
IntLockTest test = new IntLockTest();
test.doTest();
}
private void doTest() throws InterruptedException {
new Thread(new IntLockTest.IntegerRunnable()).start();
new Thread(new IntLockTest.IntegerRunnable()).start();
countDownLatch.await();
System.out.println(count);
}
CountDownLatch countDownLatch = new CountDownLatch(2);
class IntegerRunnable implements Runnable {
@Override
public void run() {
for (int j = 0; j < 100000; j++) {
// synchronized (IntegerRunnable.class) {
synchronized (count) {
count = count + 1;
}
}
countDownLatch.countDown();
}
}
}
1.2 、概念
1.2.1 、偏向锁
虽然在代码中使用了锁,但只有一个线程在调用。此时只会在对像的数据头中标识锁的标记,不会调用内核级别的锁。
1.2.2 、轻量锁虽然在代码中使用了锁,也有多个线程在交替调用,但同一时间只有一个线程调用该方法,此时只会在对象的数据头中标识锁的地址,不会调用内核级别的锁。
1.2.3 、重量锁在代码中使用了锁,有多个线程在同时调用,此时,会调用内核级别的锁。
1.2.4 、重入锁支持多次上锁,示例
void fun(){
synchronized (object) {
// todo
synchronized (object) {
// todo
}
}
}
1.2.4 、公平、非公平锁
synchronized 是java关键字,是在虚拟机内部机制处理的,本人一些小测试认为是非公平锁,不权威。
2、 ReentrantLock 2.1、 使用方法官方示例
class X {
private final ReentrantLock lock = new ReentrantLock();
// ...
public void m() {
lock.lock(); // block until condition holds
try {
// ... method body
} finally {
lock.unlock();
}
}
}
2.2 、ReentrantLock 源码
public class ReentrantLock implements Lock, java.io.Serializable {
private final Sync sync;
// 内部类,实现AQS同步队列
abstract static class Sync extends AbstractQueuedSynchronizer {
...
}
//公平锁
static final class FairSync extends Sync {
}
//非公平锁
static final class NonfairSync extends Sync {
}
//默认使用非公平锁
public ReentrantLock() {
sync = new NonfairSync();
}
//也可以通过策略来选择公平或者非公平锁
public ReentrantLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();
}
}
2.2.1、 AbstractQueuedSynchronizer
AQS同步队列,是基于FIFO队列实现同步基础框架,是CLH lock 思想的一个变种。
2.2.2 、非公平锁的实现我们默认使用 ReentrantLock lock = new ReentrantLock()得到的是非公平锁,在上锁过程中有个很重要思想:在入等待队列前,有机会就去看看能否抢到锁。
在调用lock.lock();拿锁时,会去请求非公平锁的lock();
public void lock() {
//sync 对象目前是非公平锁对象,所以调用非公平锁类方法
sync.lock();
}
static final class NonfairSync extends Sync {
private static final long serialVersionUID = 7316153563782823691L;
final void lock() {
if (compareAndSetState(0, 1))
setExclusiveOwnerThread(Thread.currentThread());
else
//去抢锁,抢不到,进队列,这里的1,可以看成是个信号量,
//可重入锁每次上锁的时候,status值加1,释放锁的时候,status值减1
acquire(1);
}
//AbstractQueuedSynchronizer.acquire方法调用过来,尝试抢锁
protected final boolean tryAcquire(int acquires) {
return nonfairTryAcquire(acquires);
}
}
public abstract class AbstractQueuedSynchronizer extends AbstractOwnableSynchronizer implements java.io.Serializable {
//子类 Sync 调用过来
public final void acquire(int arg) {
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
//如果当前线程是被唤醒的,则触发下设置在本线程中的中断点,就是一个唤醒信号,通知
selfInterrupt();
}
}
abstract static class Sync extends AbstractQueuedSynchronizer {
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;
}
}
我们在看下 addWaiter 方法的实现
// 这个mode 就是是个null的节点
private Node addWaiter(Node mode) {
Node node = new Node(Thread.currentThread(), mode);
// Try the fast path of enq; backup to full enq on failure
//这个if就是通过一个cas 把当前节点设置到锁列表的尾部,设置成功就返回,失败就进入到enq方法自旋设置,直到成功
Node pred = tail;
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 (;;) {
//首次执行的时候,tail没有值,会循环2遍,
//第一遍是初始化head和tail为一个空的Node,
//第二遍是把当前node 放到队列尾部
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 {
//中断标识,如果这个方法返回false 则不执行外面if里的触发点
boolean interrupted = false;
for (;;) {
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);
}
}
到此,非公平锁加锁流程完了。
2.2.3 公平锁的实现我们使用 ReentrantLock lock = new ReentrantLock(true);就能创建一个公平锁了,上锁的方式也是一样,对业务代码是无感的。
与非公平锁的区别部分,后续流程都一样
static final class FairSync extends Sync {
private static final long serialVersionUID = -3000897897090466540L;
final void lock() {
//抢锁的时候,不考虑当前锁状态是否空闲,直接去往队列撞
acquire(1);
}
protected final boolean tryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
if (c == 0) {
if (!hasQueuedPredecessors() &&
compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);
return true;
}
}
else if (current == getExclusiveOwnerThread()) {
int nextc = c + acquires;
if (nextc < 0)
throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
return false;
}
}
2.2.4 锁释放
我们会调用锁的unlock 方法释放锁
public void unlock() {
sync.release(1);
}
public final boolean release(int arg) {
// tryRelease 如果释放的锁状态为初始0 了,就会去唤醒下一个Node
if (tryRelease(arg)) {
//唤醒下一个Node
Node h = head;
// waitStatus 初始状态为0 ,在他后面的节点进入队列前会更新这个值,标识需要被唤醒
if (h != null && h.waitStatus != 0)
unparkSuccessor(h);
return true;
}
return false;
}
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);
}
锁队列结构
head 中不包含线程信息,在某个节点成功头的时候,代表着当前节点的线程已经在执行了,同时,在抢锁的时候,如果就一个线程,则不创建锁队列。 还有一种情况,就是锁队列里还有节点在等待,但是上个锁释放瞬间,被新进来的一个线程抢走了,那么本次唤醒就会抢不到锁,继续等待,且把head的唤醒标识符重置,等下下次唤醒。
private void setHead(Node node) {
head = node;
node.thread = null;
node.prev = null;
}
2.2.5 Condition
锁队列结构
head和tail 对锁队列
firstWaiter 和 lastWaiter 是等待队列
当使用Condition的 await(),节点进入等待队列,signal()唤醒,进入锁队列 等待抢锁,抢到锁后才真正执行。
await()等待源码解析
public final void await() throws InterruptedException {
//当前线程被中断了,直接中断异常
if (Thread.interrupted())
throw new InterruptedException();
//在等待队列里,新增一个节点
Node node = addConditionWaiter();
int savedState = fullyRelease(node);
int interruptMode = 0;
while (!isOnSyncQueue(node)) {
//检测下当前节点有没有进入锁队列,没有就挂起,等待唤醒
LockSupport.park(this);
// 唤醒后执行进队列操作
if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)
break;
}
//当前线程已经被唤醒,用savedState去还原锁状态,这里是已经进入了锁队列,抢锁
if (acquireQueued(node, savedState) && interruptMode != THROW_IE)
interruptMode = REINTERRUPT;
if (node.nextWaiter != null) // clean up if cancelled
unlinkCancelledWaiters();
if (interruptMode != 0)
reportInterruptAfterWait(interruptMode);
}
示例代码
public class ReentrantLockTest3 {
Integer count = 0;
public static void main(String[] args) throws InterruptedException {
CountDownLatch countDownLatch = new CountDownLatch(1);
ReentrantLockTest3 test = new ReentrantLockTest3();
test.doReentrantTest();
countDownLatch.await();
}
private void doReentrantTest() throws InterruptedException {
ReentrantLock lock = new ReentrantLock();
Condition consumerCondition = lock.newCondition();
Condition producerCondition = lock.newCondition();
//生产者,给count一个非0值,等待消费者消费,
new Thread(() -> {
for (; ; ) {
try {
lock.lock();
while (count > 0) {
producerCondition.await();
}
count++;
System.out.println("生产者生产:count=" + count);
Thread.sleep(1000);
consumerCondition.signal();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}).start();
//消费者,消费count ,且置0 ,等待生产者生产,
new Thread(() -> {
for (; ; ) {
try {
lock.lock();
while (count == 0) {
consumerCondition.await();
}
count--;
System.out.println("消费者消费:count=" + count);
Thread.sleep(1000);
producerCondition.signal();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}).start();
}
}
打印结果
生产者生产:count=1 消费者消费:count=0 生产者生产:count=1 消费者消费:count=0



