初识lock体系

一、Lock简介

锁用来控制多个线程访问共享资源的方式。一般,一个锁能够防止多个线程同时访问共享资源。

1.使用格式

使用格式:
Lock lock=new ReentrantLock();
try{
	lock.lock();//表示为程序上锁,lock()与synchronized一样,可以用 lockInterruptibly()/tryLock()
	//以下代码只有一个线程可以运行。
	.......
}finally{
	lock.unlock();//解锁,无论是否有异常,都会对锁进行释放。
}
catch块可选

2.内建锁和Lock体系最大的区别

内建锁是隐式地加、解锁,而Lock体系是显式地加锁和锁的消除。
多个线程同时竞争一个资源--重量级锁。synchronized  此时其它线程会阻塞。但是在lock中,其它线程会自旋,不断尝试获取锁。

释放锁的方式:synchronized会等到同步代码块执行完成或者遇到异常时,锁会自动释放;而lock必须使用unlock()才会释放锁,并且释放锁的操作应该放到finally块中执行。(保证无论是否会发生异常,锁一定会被释放,避免死锁。)

Lock体系相较于synchronized独有的方法:

a.响应中断
	void lockInterruptibly() throws InterruptedException;
b.非阻塞式获取锁
	boolean tryLock();
c.支持超时
	boolean tryLock(long time, TimeUnit unit) throws InterruptedException;

3.lock体系的特性(内建锁不具备)

拥有可中断的获取锁以及超时获取锁以及共享锁。

4.lock常用API

void lock();//获取锁
void lockInterruptibly() throws InterruptedException();//响应中断锁
boolean tryLock();//获取锁返回true,反之返回false。
boolean tryLock(long time,TimeUnit);//超时获取锁,在规定时间内未获取到锁返回false
Condition newCondition();//获取与lock绑定的等待通知组件
void unlock();//释放锁

基本上Lock的所有子类(如:ReentrantLock)中所有的方法实际上都是调用了其静态内部类Sync中的方法,而Sync类继承了AbstractQueuedSynchronized(AQS-简称同步器)。

因此对于ReentrantLock锁的理解,核心在于对队列同步器(AbstractedQueuedSynchronized)(简称同步器)的理解。

二、AQS(AbstractQueuedSynchronized)-同步器

同步器是用来构建锁以及其它同步组件的基础框架,它的实现主要是依赖一个int型的状态变量以及通过一个FIFO队列共同构成同步队列。

子类必须重写AQS的用protected修饰的用来改变同步状态的方法,其它方法主要是实现了排队与阻塞机制。

int状态的更新,使用getState()、setState()以及compareAndSetState()。

1.AQS和lock的关系

lock-面向使用者,定义了使用者与锁交互的接口。
AQS-面向锁的实现者,屏蔽了同步状态的管理、线程排队、线程等待与唤醒等等底层操作

2.AQS的模板模式

模板模式:基于抽象类
final 核心的算法
final 普通的方法(共用)
抽象方法 延迟到子类实现

AQS使用模板方法模式,将一些与状态相关的核心方法开放给子类重写,而后AQS会使用子类重写的关于状态的方法进行线程的排队、阻塞以及唤醒等操作。

子类推荐使用静态内部类来继承AQS,实现自己的同步语义。同步器既支持独占锁,也支持共享锁。

3.AQS详解

在同步组件中,AQS是最核心的部分,同步组件的实现依赖AQS提供的模板方法来实现同步组件语义。

AQS实现了对同步状态的管理,以及对阻塞线程进行排队,等待通知等等底层实现。

AQS核心组成同步队列、独占锁的获取与释放、共享锁的获取与释放以及可中断锁、超时锁。这一系列功能的实现依赖于AQS提供的模板方法。

3.1 独占锁和共享式锁

独占锁:
1.void acquire(int arg);//独占式获取同步状态,如果获取失败插入同步队列进行等待。
2.void acquireInterruptibly(int arg);//在1的基础上,此方法可以在同步队列中响应中断
3.boolean tryAcquireNanos(int arg,long nanosTimeout);//在2的基础上增加了超时等待功能,到了预计时间还未获得锁直接返回。
4.boolean tryAcquire(int arg);//获取锁成功,返回true;否则返回false。
5.boolean release(int arg);//释放同步状态,该方法会唤醒在同步队列的下一个节点。

共享式锁:
1.void acquireShared(int arg);//共享获取同步状态,同一时刻多个线程获取同步状态。
2.void acquireSharedInterruptibly(int arg);//在1的基础上,增加响应中断。
3.boolean tryAcquireSharedNanos(int arg,long nanosTimeout);//在2的基础上增加超时等待
4.Boolean releaseShared(int arg);//共享式释放同步状态

3.2 同步队列

带有头尾指针的双向链表,每一个排队的线程被封装成为Node节点。

任何一个对象都存在两个队列:

同步队列:所有获取Monitor失败的线程进入同步队列,等待获取锁。

等待队列:调用wait()阻塞的线程进入等待队列。(如:获取锁的线程调用了wait())

在AQS内部,有一个静态内部类Node,这是同步队列中每个具体的节点。
节点中有如下属性:
int waitStatus:节点状态
Node prev:同步队列中的前驱节点
Node next:同步队列中的后继节点
Thread thread:当前节点包装的线程对象
Node nextWaiter:等待队列中的下一个节点

节点状态值如下:
int INITIAL=0;//初始状态
int CANCELLED=1;//当前节点从同步队列中取消
int SIGNAL=-1;//(常考)当前节点的后继节点处于阻塞(WAIT)状态,如果当前节点处于同步状态会通知后继节点,使后继节点继续运行。
int CONDITION=-2;//表示节点处于等待队列中。当其它线程对Condition调用signal()后,该节点会从等待队列移到同步队列中。
int PROPAGATE=-3;//共享式同步状态会无条件地传播

AQS采用带有头尾节点的双向链表。

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值