jvm提供了关键字sychronized与volatile去实现线程安全。而java层面是基于juc(java.util.current)包去实现线程安全。
Lock接口
1、sychronized与lock的区别
lock与sychronized都能达到相同的效果,保证线程的原子性,可见性,一致性。但是lock比sychronied更加灵活
a.lock是jdk层面上的实现,sychronized是jvm 层面的关键字。
b. lock.lock()与lock.unlock()可以主动获取锁释放锁,sychronized是被动的释放锁
sychronized 释放锁的时机:i、代码执行执行结束 ii、产生异常了
c.lock可以判断锁的状态,sychronized 是一个关键字,没法去判断锁的状态。
d.lock可以指定公平锁和非公平锁,sychronized只能作为非公平锁
e.lock可以有共享锁与排他锁,例如读写锁的实现。而sychronized是一个可重入的排他锁
public class RWLockDemo {
//共享锁-在同一时刻可以有多个线程获得锁 在读多写少的情况下,性能好于排它锁
//读锁 写锁 (读多写少)
//读锁--读锁 共享 读锁--写锁 不共享 写锁---写锁 不共享
static Map<String ,Object> cacheMap = new HashMap<>();
static ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock();
static Lock read = readWriteLock.readLock(); //读锁
static Lock write = readWriteLock.writeLock(); //写锁
//缓存的更新和读取的时候
public static final Object get(String key){
read.lock(); //读锁 不会影响 其他的读操作
try{
return cacheMap.get(key);
}finally {
read.unlock();
}
}
public static final Object set(String key,Object value){
write.lock(); //写锁 当一个线程获取到写锁,在该线程释放锁之前,其他线程无法获取到锁(既包含读,写包含写)
try{
return cacheMap.put(key,value);
}finally {
write.unlock();
}
}
}
Lock的子类 ReentrantLock Lock类的UML 图例
AbstractQueuedSynchronizer(AQS)
aqs是一个FIFO队列
aqs 提供了两种功能 :独占锁、共享锁
Node 表示AQS链表的某个节点 类似于sychronized中的ObjectWaiter ,是AQS 的核心
AQS 结构 橙色表示head的Node节点获取锁
AQS 释放锁 断开head 与next 执行的关联 将节点回收掉,若只有两个节点,head与tail就完全相同
CAS
保证线程安全的前提下把我们的Node加入到AQS里面
private final boolean compareAndSetTail(Node expect, Node update) {
return unsafe.compareAndSwapObject(this, tailOffset, expect, update);
}
只有从内存中获取对象的值与预期值相同时,才会改变将this改变成update的值
参数说明:
this:需要改变的对象
headOffset: 调用unsafe.objectFieldOffset 通过反射获取对象的偏移量
excpt: 预期值
update :要替换的值
unsafe 在sun包里面,可以理解为后门,unsafe中的大部分方法均为native的,表示对底层系统的操作。
ReentrantLock的lock与unlock调用过程
此处分析非公平锁的获取流程
final void lock() {
if (compareAndSetState(0, 1))
setExclusiveOwnerThread(Thread.currentThread());
else
acquire(1);
}
compareAndSetState, state = 0 表示无锁 ,state>0 表示有锁
先使用case 去获取一次锁,如果能获取到锁,就把当前线程设置为exclusiveOwnerThread,表示独占(与synchronized中的_owner类似),否则执行acquire操作
public final void acquire(int arg) {
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
tryAcquire(int aquire)
protected final boolean tryAcquire(int acquires) {
return nonfairTryAcquire(acquires);
}
nonfairTryAcquire 方法
final boolean nonfairTryAcquire(int acquires) {
final Thread current = Thread.currentThread(); //获取当前线程
int c = getState(); //获取锁的状态
if (c == 0) { //0 表示无所状态
if (compareAndSetState(0, acquires)) { //再通过cas去获取锁
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;
}
acquireQueued(addWaiter(Node.EXCLUSIVE), arg)
addWaiter(Node.EXCLUSIVE) 将当前线程封装成独占状态的Node
private Node addWaiter(Node mode) {
Node node = new Node(Thread.currentThread(), mode);
// Try the fast path of enq; backup to full enq on failure
Node pred = tail;
if (pred != null) {//不为空 当前的队列里已经有同步的节点了
node.prev = pred;
if (compareAndSetTail(pred, node)) {
pred.next = node;
return node;
}
}
enq(node); // 将当前的node加到队列里面
return node;
}
// 将当前的node加到队列里面
private Node enq(final Node node) {
for (;;) { //通过自旋转的方式, 将节点加入AQS 队列里
Node t = tail;
if (t == null) { // Must initialize
if (compareAndSetHead(new Node()))
tail = head;
} else {
node.prev = t;
if (compareAndSetTail(t, node)) { //此处使用caompareAndSet来保证线程的安全性
t.next = node;
return t;
}
}
}
}
for(;;)生成的字节指令,要比while(true)生成的字节指令少,指令少能减少寄存器的存储
当两个线程 竞争 enq(node) 就会把队列改成这个样子
acquireQueued
final boolean acquireQueued(final Node node, int arg) {
boolean failed = true;
try {
boolean interrupted = false;
for (;;) {
final Node p = node.predecessor(); //获得node的前一个节点
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);
}
}
当满足if (p == head && tryAcquire(arg))时
橙色表示已经释放锁的节点,被孤立,要删除掉的节点
shouldParkAfterFailedAcquire 获取锁失败后,判断线程是不是要挂起
private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
int ws = pred.waitStatus;
if (ws == Node.SIGNAL)
/*
* This node has already set status asking a release
* to signal it, so it can safely park.
*/
return true;
if (ws > 0) {
/*
* Predecessor was cancelled. Skip over predecessors and
* indicate retry.
*/
do {
node.prev = pred = pred.prev;
} while (pred.waitStatus > 0);
pred.next = node;
} else {
/*
* waitStatus must be 0 or PROPAGATE. Indicate that we
* need a signal, but don't park yet. Caller will need to
* retry to make sure it cannot acquire before parking.
*/
compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
}
return false;
}
parkAndCheckInterrupt 挂起线程,并设置复位
private final boolean parkAndCheckInterrupt() {
LockSupport.park(this);
return Thread.interrupted();
}
LockSupport.park(this); // LockSupport 主要有两个方法park 与 unpark 是unsafe 调用native 实现的的。类似于wait与notify
释放锁的过程
unlock 过程 调用release
public final boolean release(int arg) {
if (tryRelease(arg)) {
Node h = head;
if (h != null && h.waitStatus != 0)
unparkSuccessor(h);
return true;
}
return false;
}
tryRelease 方法
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;
}
unparkSuccessor 方法
private void unparkSuccessor(Node node) {
/*
* If status is negative (i.e., possibly needing signal) try
* to clear in anticipation of signalling. It is OK if this
* fails or if status is changed by waiting thread.
*/
int ws = node.waitStatus;
if (ws < 0)
compareAndSetWaitStatus(node, ws, 0);
/*
* Thread to unpark is held in successor, which is normally
* just the next node. But if cancelled or apparently null,
* traverse backwards from tail to find the actual
* non-cancelled successor.
*/
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);
}
公平锁与非公平锁的区别,非公平锁比公平锁多了一个cas操作,非公平锁在lcok()方法时,第一步直接调用CAS去获取锁,这对已经排队的线程不公平。公平锁根据队列的顺序去获取lock.
非公平锁的实现
/**
* Sync object for non-fair locks
*/
static final class NonfairSync extends Sync {
private static final long serialVersionUID = 7316153563782823691L;
/**
* Performs lock. Try immediate barge, backing up to normal
* acquire on failure.
*/
final void lock() {
if (compareAndSetState(0, 1))
setExclusiveOwnerThread(Thread.currentThread());
else
acquire(1);
}
protected final boolean tryAcquire(int acquires) {
return nonfairTryAcquire(acquires);
}
}
公平锁的实现
/**
* Sync object for fair locks
*/
static final class FairSync extends Sync {
private static final long serialVersionUID = -3000897897090466540L;
final void lock() {
acquire(1);
}
/**
* Fair version of tryAcquire. Don't grant access unless
* recursive call or no waiters or is first.
*/
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;
}
}
总结: Lock的核心就是AQS队列,CAS算法,LockSupport中的park与unpark方法。
Condition
condition 是JUC里面提供的对锁的控制,condition的await()方法, 与condition的singinal()方法和Object中的wait()和notify()等价。
Condition队列用来 存储调用await的线程。
condition队列与AQS队列的转化和调用过程,当线程调用condiion.await()时封装该线程的Node节点会从AQS队列,加入到Condition队列中,当调用condition.signal()时,在Condition队列中的线程会重新回到AQS中。
并发编程总结: