一、重入锁理解
- 某线程获取锁后,可以再次获取锁
- synchronized 和 ReentrantLock 都是可重入锁
- 可重入锁解决同一线程中内外层方法调用死锁的问题
二、可重入锁示例
1、synchronized 可重入锁演示
/**
* synchronized 重入锁示例
*/
private static void reLockSyncDemo(){
new Thread(new Runnable() {
@Override
public void run() {
synchronized (this){
System.out.println("第1次获取锁,当前锁是:" + this);
int index=0;
while(true){
synchronized (this){
System.out.println("第" + (++index) + "次获取重入锁,当前锁是:" + this);
}
if (index == 5) {
break;
}
}
}
}
}).start();
}
2、ReentronLock 可重入锁演示
/**
* ReentrantLock 重入锁示例
*/
private static void reentrantLockDemo(){
ReentrantLock rl = new ReentrantLock();
new Thread(new Runnable() {
@Override
public void run() {
rl.lock();
try {
System.out.println("第1次获取锁,当前锁是:" + rl);
int index = 0;
while (true) {
rl.lock();
try {
System.out.println("第" + (++index) + "次获取重入锁,当前锁是:" + rl);
if (index == 5) {
break;
}
} finally {
rl.unlock();
}
}
}finally{
rl.unlock();
}
}
}).start();
}
ReentronLock 在多层可重入锁调用时,lock数必须与unlock数对等,否则会出现死锁
三、可重入锁简单实现
/**
* MyRelock
* Description 自定义实现重入锁简单实现,只完成了 lock和unlock
* Create by wangzx
* Date 2021年10月18日 15:43:00
*/
public class MyRelock implements Lock {
//是否已经上锁
private boolean isLocked = false;
//记录当前线程
Thread currThread = null;
//记录当前锁数量
private int lockNum = 0;
@Override
public void lock() {
//获取当前线程
Thread thread = Thread.currentThread();
//当前已上锁 且 当前线程不是已上锁线程 进入循环等待
while (isLocked && thread!= currThread ){
try {
Thread.sleep(50);
}catch (InterruptedException e){
e.printStackTrace();
}
}
// 锁状态置为true
isLocked = true;
// 记录当前线程
currThread = thread;
// 锁记录数 加一
lockNum++;
}
@Override
public void unlock() {
// 判断是否为当前锁的线程
if(Thread.currentThread()==currThread){
// 锁记录 --
lockNum--;
if (lockNum == 0){
isLocked = false;
}
}
}
@Override
public void lockInterruptibly() throws InterruptedException {
}
@Override
public boolean tryLock() {
return false;
}
@Override
public boolean tryLock(long time, TimeUnit unit) throws InterruptedException {
return false;
}
@Override
public Condition newCondition() {
return null;
}
测试
MyRelock myRelock = new MyRelock();
private static void testMyRelock(){
Demo demo = new Demo();
new Thread(new Runnable() {
@Override
public void run() {
demo.functionA();
}
}).start();
}
private void functionA(){
myRelock.lock();
System.out.println("functionA 执行了,锁为:"+myRelock);
functionB();
myRelock.unlock();
}
private void functionB(){
myRelock.lock();
System.out.println("functionB 执行了,锁为:"+myRelock);
myRelock.unlock();
}
四、synchronized可重入实现原理
每一个锁关联一个线程持有者和一个计数器,当计数器为0时表示当前锁没有被任何线程持有,此时任何线程都可以获取锁并执行相应的代码;当某一个线程获取锁成功后,jvm就会记录当前次有锁的线程,并将计数器加1;此时其他线程请求当前锁,则需要等待,如果持有当前锁的线程再次请求当前锁,可以获得当前锁,并且计数器加1,当锁内容执行完成后,计数器减1,如果当前计数器为0时,则线程持有者置为null,此时释放锁,唤醒等待线程。
五 、ReentronLock 可重入实现
// 同步队列,是一个带头结点的双向链表,用于实现锁的语义
public abstract class AbstractQueuedSynchronizer
extends AbstractOwnableSynchronizer
implements java.io.Serializable {
// 重入锁计数/许可证数量,在不同的锁中,使用方式有所不同
private volatile int state;
//CAS方式更新状态
protected final boolean compareAndSetState(int expect, int update) {
// See below for intrinsics setup to support this
return unsafe.compareAndSwapInt(this, stateOffset, expect, update);
}
}
public class ReentrantLock implements Lock, java.io.Serializable {
/*
* 同步队列的实现者,实现了锁的语义
*
* 许可证数量==0:当前锁空闲
* 许可证数量>0,当前锁被某一线程持有
* 一个线程多次进入锁时,许可证数量会递增
* 当线程释放锁时,许可证数量减少,直到为0
*/
abstract static class Sync extends AbstractQueuedSynchronizer {
private static final long serialVersionUID = -5179523762034025860L;
/**
* Performs {@link Lock#lock}. The main reason for subclassing
* is to allow fast path for nonfair version.
*/
abstract void lock();
/**
* Performs non-fair tryLock. tryAcquire is implemented in
* subclasses, but both need nonfair try for trylock method.
*/
// 申请一次非公平锁,返回值代表锁是否申请成功
final boolean nonfairTryAcquire(int acquires) {
// 获取生成锁的线程
final Thread current = Thread.currentThread();
// 当前许可证数量 当前锁状态
int c = getState();
// 如果锁没有被任何线程占用
if (c == 0) {
/* 发现锁空闲时,不管【同步队列】中有没有人抢锁,该线程直接尝试抢锁,这也是"非公平"所在 */
// 尝试更新许可证数量为acquires,返回true代表更新成功,即成功抢到了锁
if (compareAndSetState(0, acquires)) {
// 设置当前线程为<占有者线程>
setExclusiveOwnerThread(current);
return true;
}
}
/* 至此,说明锁已被占用 */
// 如果当前线程不是锁的占用者,直接返回false,代表抢锁失败,该线程需要去排队
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;
}
// 释放一次锁,返回值表示同步锁是否处于自由状态(无线程持有)
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;
}
}
}
上面是ReentronLock实现的部分代码## 一、重入锁理解
- 某线程获取锁后,可以再次获取锁
- synchronized 和 ReentrantLock 都是可重入锁
- 可重入锁解决同一线程中内外层方法调用死锁的问题
二、可重入锁示例
1、synchronized 可重入锁演示
/**
* synchronized 重入锁示例
*/
private static void reLockSyncDemo(){
new Thread(new Runnable() {
@Override
public void run() {
synchronized (this){
System.out.println("第1次获取锁,当前锁是:" + this);
int index=0;
while(true){
synchronized (this){
System.out.println("第" + (++index) + "次获取重入锁,当前锁是:" + this);
}
if (index == 5) {
break;
}
}
}
}
}).start();
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mceqrTCf-1634629291378)(/Users/lewis/Library/Application Support/typora-user-images/image-20211018153755361.png)]
2、ReentronLock 可重入锁演示
/**
* ReentrantLock 重入锁示例
*/
private static void reentrantLockDemo(){
ReentrantLock rl = new ReentrantLock();
new Thread(new Runnable() {
@Override
public void run() {
rl.lock();
try {
System.out.println("第1次获取锁,当前锁是:" + rl);
int index = 0;
while (true) {
rl.lock();
try {
System.out.println("第" + (++index) + "次获取重入锁,当前锁是:" + rl);
if (index == 5) {
break;
}
} finally {
rl.unlock();
}
}
}finally{
rl.unlock();
}
}
}).start();
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NeCrmrZm-1634629291379)(/Users/lewis/Library/Application Support/typora-user-images/image-20211018153908473.png)]
ReentronLock 在多层可重入锁调用时,lock数必须与unlock数对等,否则会出现死锁
三、可重入锁简单实现
/**
* MyRelock
* Description 自定义实现重入锁简单实现,只完成了 lock和unlock
* Create by wangzx
* Date 2021年10月18日 15:43:00
*/
public class MyRelock implements Lock {
//是否已经上锁
private boolean isLocked = false;
//记录当前线程
Thread currThread = null;
//记录当前锁数量
private int lockNum = 0;
@Override
public void lock() {
//获取当前线程
Thread thread = Thread.currentThread();
//当前已上锁 且 当前线程不是已上锁线程 进入循环等待
while (isLocked && thread!= currThread ){
try {
Thread.sleep(50);
}catch (InterruptedException e){
e.printStackTrace();
}
}
// 锁状态置为true
isLocked = true;
// 记录当前线程
currThread = thread;
// 锁记录数 加一
lockNum++;
}
@Override
public void unlock() {
// 判断是否为当前锁的线程
if(Thread.currentThread()==currThread){
// 锁记录 --
lockNum--;
if (lockNum == 0){
isLocked = false;
}
}
}
@Override
public void lockInterruptibly() throws InterruptedException {
}
@Override
public boolean tryLock() {
return false;
}
@Override
public boolean tryLock(long time, TimeUnit unit) throws InterruptedException {
return false;
}
@Override
public Condition newCondition() {
return null;
}
测试
MyRelock myRelock = new MyRelock();
private static void testMyRelock(){
Demo demo = new Demo();
new Thread(new Runnable() {
@Override
public void run() {
demo.functionA();
}
}).start();
}
private void functionA(){
myRelock.lock();
System.out.println("functionA 执行了,锁为:"+myRelock);
functionB();
myRelock.unlock();
}
private void functionB(){
myRelock.lock();
System.out.println("functionB 执行了,锁为:"+myRelock);
myRelock.unlock();
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-24wCcr7q-1634629291380)(/Users/lewis/Library/Application Support/typora-user-images/image-20211018163405154.png)]
四、synchronized可重入实现原理
每一个锁关联一个线程持有者和一个计数器,当计数器为0时表示当前锁没有被任何线程持有,此时任何线程都可以获取锁并执行相应的代码;当某一个线程获取锁成功后,jvm就会记录当前次有锁的线程,并将计数器加1;此时其他线程请求当前锁,则需要等待,如果持有当前锁的线程再次请求当前锁,可以获得当前锁,并且计数器加1,当锁内容执行完成后,计数器减1,如果当前计数器为0时,则线程持有者置为null,此时释放锁,唤醒等待线程。
五 、ReentronLock 可重入实现
// 同步队列,是一个带头结点的双向链表,用于实现锁的语义
public abstract class AbstractQueuedSynchronizer
extends AbstractOwnableSynchronizer
implements java.io.Serializable {
// 重入锁计数/许可证数量,在不同的锁中,使用方式有所不同
private volatile int state;
//CAS方式更新状态
protected final boolean compareAndSetState(int expect, int update) {
// See below for intrinsics setup to support this
return unsafe.compareAndSwapInt(this, stateOffset, expect, update);
}
}
public class ReentrantLock implements Lock, java.io.Serializable {
/*
* 同步队列的实现者,实现了锁的语义
*
* 许可证数量==0:当前锁空闲
* 许可证数量>0,当前锁被某一线程持有
* 一个线程多次进入锁时,许可证数量会递增
* 当线程释放锁时,许可证数量减少,直到为0
*/
abstract static class Sync extends AbstractQueuedSynchronizer {
private static final long serialVersionUID = -5179523762034025860L;
/**
* Performs {@link Lock#lock}. The main reason for subclassing
* is to allow fast path for nonfair version.
*/
abstract void lock();
/**
* Performs non-fair tryLock. tryAcquire is implemented in
* subclasses, but both need nonfair try for trylock method.
*/
// 申请一次非公平锁,返回值代表锁是否申请成功
final boolean nonfairTryAcquire(int acquires) {
// 获取生成锁的线程
final Thread current = Thread.currentThread();
// 当前许可证数量 当前锁状态
int c = getState();
// 如果锁没有被任何线程占用
if (c == 0) {
/* 发现锁空闲时,不管【同步队列】中有没有人抢锁,该线程直接尝试抢锁,这也是"非公平"所在 */
// 尝试更新许可证数量为acquires,返回true代表更新成功,即成功抢到了锁
if (compareAndSetState(0, acquires)) {
// 设置当前线程为<占有者线程>
setExclusiveOwnerThread(current);
return true;
}
}
/* 至此,说明锁已被占用 */
// 如果当前线程不是锁的占用者,直接返回false,代表抢锁失败,该线程需要去排队
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;
}
// 释放一次锁,返回值表示同步锁是否处于自由状态(无线程持有)
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;
}
}
}
上面是ReentronLock实现的部分代码