大家好,又见面了,我是你们的朋友全栈君。
重入锁,顾名思义,就是支持重进入的锁,它表示该锁能够支持一个线程对资源的重复加锁。重进入是指任意线程在获取到锁之后能够再次获取该锁而不会被锁阻塞,该特性的实现需要解决以下两个问题。
- 1、线程再次获取锁。锁需要去识别获取锁的线程是否为当前占据锁的线程,如果是,则再次成功获取。
- 2、锁的最终释放。线程重复n次获取了锁,随后在第n次释放该锁后,其他线程能够获取到该锁。锁的最终释放要求锁对于获取进行计数自增,计数表示当前锁被重复获取的次数,而锁被释放时,计数自减,当计数等于0时表示锁已经成功释放。
- Java里面内置锁(synchronize)和Lock(ReentrantLock)都是可重入的
关键字synchronized拥有锁重入的功能,也就是在使用synchronized时,当一个线程得到了一个对象的锁后,再次请求此对象时是可以再次得到该对象的锁。
package com.xiaoyexinxin.ThreadLearn;
/**
* 锁重入
*
*/
public class SyncDubbo1 {
public synchronized void method1(final SyncDubbo1 sd){
System.out.println("线程"+Thread.currentThread().getName()+"进入方法1");
if(Thread.currentThread().getName().equals("t1")){
Thread t3=new Thread(new Runnable() {
public void run() {
// TODO Auto-generated method stub
sd.method2();
}
},"t3");
t3.start();
System.out.println("线程t1的method1方法执行..");
method2();
System.out.println("线程t1的method1调用结束");
} else if(Thread.currentThread().getName().equals("t2")){
System.out.println("线程t2的method1方法执行..");
method2();
System.out.println("线程t2的method1调用结束");
}
}
public void method2() {
System.out.println("线程"+Thread.currentThread().getName()+"进入方法2");
if(Thread.currentThread().getName().equals("t1")){
System.out.println("线程t1的method2方法执行..");
method3();
System.out.println("线程t1的method2调用结束");
}else if(Thread.currentThread().getName().equals("t2")){
System.out.println("线程t2的method2方法执行..");
method3();
System.out.println("线程t2的method2调用结束");
}else if(Thread.currentThread().getName().equals("t3")){
System.out.println("线程t3的method2方法执行..");
method3();
System.out.println("线程t3的method2调用结束");
}
}
public void method3(){
System.out.println("线程"+Thread.currentThread().getName()+"进入方法3");
if(Thread.currentThread().getName().equals("t1")){
System.out.println("线程t1的method3方法执行完毕");
}else if(Thread.currentThread().getName().equals("t2")){
System.out.println("线程t2的method3方法执行完毕");
}else if(Thread.currentThread().getName().equals("t3")){
System.out.println("线程t3的method3方法执行完毕");
}
}
public static void main(String[] args) {
// TODO Auto-generated method stub
final SyncDubbo1 s=new SyncDubbo1();
Thread t1=new Thread(new Runnable() {
public void run() {
// TODO Auto-generated method stub
s.method1(s);
}
},"t1");//t1这个参数是定义线程的名字
Thread t2=new Thread(new Runnable() {
public void run() {
// TODO Auto-generated method stub
s.method1(s);
}
},"t2");
t1.start();
t2.start();
}
}
执行结果:
线程t1进入方法1
线程t1的method1方法执行..
线程t1进入方法2
线程t1的method2方法执行..
线程t1进入方法3
线程t1的method3方法执行完毕
线程t1的method2调用结束
线程t1的method1调用结束
线程t2进入方法1
线程t2的method1方法执行..
线程t3进入方法2
线程t2进入方法2
线程t3的method2方法执行..
线程t2的method2方法执行..
线程t2进入方法3
线程t3进入方法3
线程t2的method3方法执行完毕
线程t2的method2调用结束
线程t2的method1调用结束
线程t3的method3方法执行完毕
线程t3的method2调用结束
可以看到线程t1和t3执行method2和method3的顺序是不固定的,这样就可能有问题,在处理可能出现线程问题的情况时,我们更希望线程执行的时候不要交叉执行,那么我们可以在method2和method3上加synchronized关键字,
修改后的执行结果是:
线程t1进入方法1
线程t1的method1方法执行..
线程t1进入方法2
线程t1的method2方法执行..
线程t1进入方法3
线程t1的method3方法执行完毕
线程t1的method2调用结束
线程t1的method1调用结束
线程t3进入方法2
线程t3的method2方法执行..
线程t3进入方法3
线程t3的method3方法执行完毕
线程t3的method2调用结束
线程t2进入方法1
线程t2的method1方法执行..
线程t2进入方法2
线程t2的method2方法执行..
线程t2进入方法3
线程t2的method3方法执行完毕
线程t2的method2调用结束
线程t2的method1调用结束
这样就有序执行了。
可以看到线程t1执行完之后才执行的线程t3,最后执行的是线程t2,线程t1由方法method1要去执行由synchronized修饰的method2,直接便可以获取到锁,这种情况便是锁重入。如果synchronized不支持锁重入的话,会造成死锁的情况(method1还没执行完,要执行method2,method2不让获取锁的话,method1就执行不完了)。
可见重入锁最大的作用是避免死锁
上面说的是一种锁重入的场景,锁重入还有一种常见的情形,那就是父子类的情况,再看一个例子
package com.xiaoyexinxin.ThreadLearn;
public class SyncDubbo2 {
static class Main{
int i=10;
public synchronized void operationed(){
i--;
System.out.println("父类i="+i);
try {
Thread.sleep(100);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
static class Sub extends Main{
public synchronized void operationed(){
while(i>0){
i--;
System.out.println("子类i="+i);
try {
Thread.sleep(100);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
super.operationed();
}
}
}
public static void main(String[] args) {
// TODO Auto-generated method stub
Thread t1=new Thread(new Runnable() {
public void run() {
// TODO Auto-generated method stub
Sub sub=new Sub();
sub.operationed();
}
});
t1.start();
}
}
执行结果:
子类i=9
父类i=8
子类i=7
父类i=6
子类i=5
父类i=4
子类i=3
父类i=2
子类i=1
父类i=0
执行结果如下,可以看到Sub和Main类的方法交替执行,而这两个方法都有synchronized修饰,说明父子类的情况锁重入也是可以的。
发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/106149.html原文链接:https://javaforall.cn
【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛
【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...