Lock锁在java.util.lock里面。
lock——面向使用者,定义使用者与锁交互的接口
AQS——面向锁的实现者,屏蔽了同步状态的管理、线程排队、线程等待与唤醒等等底层操作。
一、Lock简介
1.使用Lock加锁、释放锁
Lock lock = new ReentrantLock();
try{
lock.lock();//加锁
//以下代码只有一个线程可以运行
...
}finally{
lock.unlock();//解锁
}
例子:
package com.wschase.lock;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
* Author:WSChase
* Created:2019/1/10
*/
class MyThread implements Runnable{
private int tick=500;
private Lock lock=new ReentrantLock();
@Override
public void run() {
for(int i=0;i<500;i++){
try{
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
lock.lock();
if(tick>0){
System.out.println(Thread.currentThread().getName()+"还剩余"+tick--+"票");
}
}finally {
lock.unlock();
}
}
}
}
public class TestLock {
public static void main(String[] args) {
MyThread mythread = new MyThread();
Thread thread1=new Thread(mythread,"A");
Thread thread2=new Thread(mythread,"B");
Thread thread3=new Thread(mythread,"C");
thread1.start();
thread2.start();
thread3.start();
}
}
2.lock常用API
lock体系拥有可中断的获取锁以及超时获取锁以及共性锁等内建锁不具备的特性。
void lock();//获取锁
void unlock();//释放锁
void lockIntteruptibly() throws InterruptedException();//响应中断锁
boolean tryLock();//获取锁返回true,反之返回false
boolean tryLock(long time,TimUnit unit);//超时获取锁,在规定时间内获取到返回false
Condition newCondition();//获取与lock绑定的等待通知组件
lock中所有的方法实际上都是调用了其静态内部类Sync中的方法,而Sync继承了AbstractQueueSynchronized(AQS-简称同步器)
二、AQS-同步器
1.概念:同步器是用来构建锁以及其他同步组件的基础框架,它的实现主要是依赖一个int状态变量以及通过一个FIFO队列共同构成同步队列。
子类必须重写AQS的用protected修饰的用来改变同步状态的方法,其他方法主要实现了排队与阻塞机制。int状态的更新使用getState()、setState()、compareState()。
2.子类推荐使用静态内部类来继承AQS实现自己的同步语义。同步器即支持独占锁,也支持共享锁。
3.AQS的模板模式
AQS使用模板方法模式,将一些与状态相关的核心方法开放给子类重写,而后AQS会使用子类重写的关于状态的方法进行线程的排队、阻塞以及唤醒等操作。
例子:通过AQS自定义一个锁
package com.wschase.lock;
import java.time.LocalDateTime;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.AbstractQueuedSynchronizer;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
/**AQS模板例子——自己实现一个锁
* Author:WSChase
* Created:2019/1/10
*/
class Mutex implements Lock {
private Sync sync=new Sync();
//自定义同步器
static class Sync extends AbstractQueuedSynchronizer{
@Override
protected boolean tryAcquire(int arg) {
if(arg!=1){
throw new RuntimeException("arg参数不为1");
}
//获取锁:从0变成1
if(compareAndSetState(0,1)){
//此时线程成功获取同步状态
setExclusiveOwnerThread(Thread.currentThread());
return true;
}
return false;
}
@Override
protected boolean tryRelease(int arg) {
//释放锁:从1变成0
if(getState()==0){
throw new IllegalMonitorStateException();
}
setExclusiveOwnerThread(null);
setState(0);
return true;
}
@Override
protected boolean isHeldExclusively() {
return getState()==1;//为1表示持有线程
}
}
//---------------------------------------------------------------
@Override
public void lock() {
sync.acquire(1);//调用acquire来上锁
}
@Override
public void lockInterruptibly() throws InterruptedException {
sync.acquireInterruptibly(1);
}
@Override
public boolean tryLock() {
return sync.tryAcquire(1);
}
@Override
public boolean tryLock(long time, TimeUnit unit) throws InterruptedException {
return sync.tryAcquireNanos(1,time);
}
@Override
public void unlock() {
sync.release(1);
}
@Override
public Condition newCondition() {
return null;
}
//----------------------------------------------------------------
}
public class TestAQSmoban {
public static void main(String[] args) {
Lock lock=new Mutex();
for(int i=0;i<10;i++){
Thread thread=new Thread(()-> {
try{
lock.lock();
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
} finally{
lock.unlock();
}
});
thread.start();
}
}
}
通过上面的例子我们再总结锁与AQS的关系:
(1)锁面对使用者,定义了使用者与锁的交互的接口。同步器面向锁的实现,简化了锁的实现方式,屏蔽同步状态管理,线程排队、等待、唤醒等操作。
(2)任何一个实现了Lock接口的子类内部一定有一个静态内部类继承了AQS来进行同步语义的定义,即你需要告诉我我的同步器传几认为是拿到锁了。
4.AQS详解
*在同步组件中QAQ是最核心的部分,同步组件的实现依赖AQS提供的模板方法来实现同步组件语义。
*AQS实现了对同步状态的管理,以及对阻塞线程进行排队、等待通知等等底层实现。
*AQS核心组成:同步队列、独占锁的获取与释放、共享锁的获取与释放、可中断锁、超时锁。这一系列功能的实现依赖AQS提供的模板方法。
(1)独占锁:
1).void acquire(int arg):
独占式获取同步状态,如果获取失败插入同步队列进行等待。
2).void acpuireInterruptibly(int arg):
独占式获取锁,但是此方法可以在同步队列中响应中断
3).boolean tryAccquireNanos(int arg,long nanosTimeOut):
在2的基础上增加了超时等待功能,到了预计时间还未获得锁直接返回。
2、3是AQS提供的独有功能
4).boolean tryAcquire(int arg):
获取锁成功返回true,否则返回false
5).boolean release(int arg):
释放同步状态,该方法会唤醒在同步队列的下一个节点。
(2)共享锁式:
1).void acquireShared(int arg):
共享获取同步状态,同一时刻多个线程获取同步状态
2).void acpuireSharedInterruptibly(int arg):
在1的基础上增加同步队列中响应中断
3).boolean tryAccquireSharedNanos(int arg,long nanosTimeOut):
在2的基础上增加了超时等待功能,到了预计时间还未获得锁直接返回。
2、3是AQS提供的独有功能
4).boolean releaseShared(int arg):共享式释放同步状态
(3)同步队列
在AQS内部有一个静态内部类Node,这是同步队列中每个具体的节点。
1)节点中有如下属性:
int waitStatus:节点状态
Node prev:前驱节点
Node next:后继节点
Thread thread:当前节点包装的线程对象
Node nextWaiter:等待队列中的下一个节点。
2)节点状态值如下:
int INITIAL=0;//初始状态
int CANCELLED=1;//当前节点从同步队列中取消
int SIGNAL=-1;//后继节点处于等待状态,如果当前节点释放同步状态会通知后继节点,使后继节点继续运行。(面试最爱考的状态)
int CONDITION=-2;//表示节点处于等待队列中。当其他线程对Condition调用signal()方法后,该节点会从等待队列移到同步队列中
int PROPAGATE=-3;//共享同步状态会无条件的传播
简而言之,AQS同步队列的底层采用带有头尾节点的双向链表。
三、ReentrantLock(可重入锁)
四、ReentrandReadWriteLock(可重入读写锁)
五、Condition机制
六、LockSupport