可重入锁(递归锁)
可重入锁
synchronized 和Lock都是可重入锁,也叫递归锁,即一个线程可以重复获取同一把锁
是指在同一个线程在外层方法获取锁的时候,再进入该线程的内层方法会自动获取锁(前提,锁对象得是同一个对象),不会因为之前已经获取过还没释放而阻塞。
Java中ReentrantLock和synchronized都是可重入锁,可重入锁的一个优点是可一定程度避免死锁。
synchronized 是隐式锁,Lock是显式锁
即synchronized 加锁解锁自动完成
Lock加锁解锁要手动完成
synchronized
synchronized 可重入锁(同步代码块)
package com.dongguo.sync;
/** * @author Dongguo * @date 2021/8/24 0024-13:58 * @description:synchronized 可重入锁 */
public class SyncLockDemo {
public static void main(String[] args) {
Object object = new Object();
new Thread(() -> {
synchronized (object) {
System.out.println(Thread.currentThread().getName() + "-外层");
synchronized (object) {
System.out.println(Thread.currentThread().getName() + "-中层");
synchronized (object) {
System.out.println(Thread.currentThread().getName() + "-内层");
}
}
}
}, "ThreadA").start();
}
}
运行结果:
ThreadA-外层
ThreadA-中层
ThreadA-内层
synchronized 递归锁(同步方法)
package com.dongguo.sync;
/** * @author Dongguo * @date 2021/8/24 0024-13:58 * @description: synchronized 可重入锁 */
public class SyncLockDemo {
public static void main(String[] args) {
new SyncLockDemo().add();
}
public long number = 0;
//递归
public synchronized void add(){
number++;
System.out.println(number);
add();//自己调自己
}
}
Synchronized的重入的实现原理
每个锁对象拥有一个锁计数器和一个指向持有该锁的线程的指针。
当执行monitorenter时,如果目标锁对象的计数器为零,那么说明它没有被其他线程所持有,Java虚拟机会将该锁对象的持有线程设置为当前线程,并且将其计数器加1。
在目标锁对象的计数器不为零的情况下,如果锁对象的持有线程是当前线程,那么 Java 虚拟机可以将其计数器加1,否则需要等待,直至持有线程释放该锁。
当执行monitorexit时,Java虚拟机则需将锁对象的计数器减1。计数器为零代表锁已被释放。
ReentrantLock
ReentrantLock可重入锁
ReentrantLock相对于 synchronized 它具备如下特点
可中断
可以设置超时时间
可以设置为公平锁
支持多个条件变量
与 synchronized 一样,都支持可重入
package com.dongguo.sync;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/** * @author Dongguo * @date 2021/8/24 0024-13:58 * @description: Lock 可重入锁 */
public class SyncLockDemo {
public static void main(String[] args) {
Lock lock = new ReentrantLock();
new Thread(() -> {
try {
lock.lock();
System.out.println(Thread.currentThread().getName() + "-外层");
try {
lock.lock();
System.out.println(Thread.currentThread().getName() + "-中层");
try {
lock.lock();
System.out.println(Thread.currentThread().getName() + "-内层");
} finally {
lock.unlock();
}
} finally {
lock.unlock();
}
} finally {
lock.unlock();
}
}, "ThreadA").start();
}
}
运行结果:
ThreadA-外层
ThreadA-中层
ThreadA-内层
注意:加锁几次就要解锁几次
package com.dongguo.sync;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/** * @author Dongguo * @date 2021/8/24 0024-13:58 * @description: */
public class SyncLockDemo1 {
public static void main(String[] args) {
Lock lock = new ReentrantLock();
new Thread(() -> {
try {
lock.lock();
System.out.println(Thread.currentThread().getName() + "-外层");
try {
lock.lock();
System.out.println(Thread.currentThread().getName() + "-中层");
try {
lock.lock();
System.out.println(Thread.currentThread().getName() + "-内层");
} finally {
lock.unlock();
}
} finally {
//lock.unlock(); //这里故意注释,实现加锁次数和释放次数不一样
//由于加锁次数和释放次数不一样,第二个线程始终无法获取到锁,导致一直在等待。
}
} finally {
lock.unlock();
}
}, "t1");
new Thread(() -> {
try {
lock.lock();
System.out.println("t2获得lock锁");
} finally {
lock.unlock();
}
}, "t2");
}
}
由于t1获取锁与释放锁的次数不同,t2无法获得lock锁
ReentrantLock递归锁
package com.dongguo.sync;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/** * @author Dongguo * @date 2021/8/24 0024-13:58 * @description: */
public class SyncLockDemo {
Lock lock = new ReentrantLock();
public static void main(String[] args) {
new SyncLockDemo().add();
}
public long number = 0;
//递归
public void add(){
try {
lock.lock();
number++;
System.out.println(number);
add();//自己调自己
} finally {
lock.unlock();
}
}
}
运行结果:
还没有评论,来说两句吧...