重入锁实现细节
在学习重入锁之前,我们先来了解一下可重入锁ReenTrantLock的实现细节。分两种:1.公平锁模型和非公平锁模型
- 公平锁:把锁比作只有一个入口的井,获取锁的过程比作打水权。初始化时,state=0, 村民A获得打水权,并将state置为state+1(变为1)。此时村民B来打水,但是只有一个井口,只能生成节点进行等待。这时村民A的家人来打水,可直接获得打水权,并将state+1(变为2),当村民A的家族有一人打完水时,satae-1;这样直到state重新为0,这时村民B获得通知,可以去打水,将打水权交给了B。村民A获得打水权(锁)时,家族人都可以打水,这就是锁重入。
- 非公平锁:如果在A打完水,系统通知B这一段时间,村民C来打水,如果是公平锁,C要等待排队,参与锁竞争。如果是非公平锁,C在系统通知B的过程中刚刚好来请求,则可在这个短暂的空闲时间内拿到锁,而不同排队了。
package com.internet.thread;
public class SyncDubbo1 {
public synchronized void method1(SyncDubbo1 sd){
if(Thread.currentThread().getName().equals("t1")){
Thread t3 = new Thread(new Runnable() {
@Override
public void run() {
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() {
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(){
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){
final SyncDubbo1 sd = new SyncDubbo1();
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
sd.method1(sd);
}
},"t1");
t1.start();
Thread t2 = new Thread(new Runnable() {
@Override
public void run() {
sd.method1(sd);
}
},"t2");
t2.start();
}
}
执行结果:
线程t1的method1方法执行..
线程t1的method2方法执行..
线程t1的method3方法执行完毕
线程t1的method2调用结束
线程t1的method1调用结束
线程t2的method1方法执行..
线程t3的method2方法执行..
线程t2的method2方法执行..
线程t3的method3方法执行完毕
线程t3的method2调用结束
线程t2的method3方法执行完毕
线程t2的method2调用结束
线程t2的method1调用结束
当method2和method3没有添加synchronized关键字,其运行结果是没有固定顺序的,这就可能造成数据错误,当method2和method3添加synchronized关键字时
package com.internet.thread;
public class SyncDubbo1 {
public synchronized void method1(SyncDubbo1 sd){
if(Thread.currentThread().getName().equals("t1")){
Thread t3 = new Thread(new Runnable() {
@Override
public void run() {
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 synchronized void method2() {
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 synchronized void method3(){
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){
final SyncDubbo1 sd = new SyncDubbo1();
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
sd.method1(sd);
}
},"t1");
t1.start();
Thread t2 = new Thread(new Runnable() {
@Override
public void run() {
sd.method1(sd);
}
},"t2");
t2.start();
}
}
运行结果
线程t1的method1方法执行..
线程t1的method2方法执行..
线程t1的method3方法执行完毕
线程t1的method2调用结束
线程t1的method1调用结束
线程t3的method2方法执行..
线程t3的method3方法执行完毕
线程t3的method2调用结束
线程t2的method1方法执行..
线程t2的method2方法执行..
线程t2的method3方法执行完毕
线程t2的method2调用结束
线程t2的method1调用结束
这是因为当t1有执行method1方法时,获得对象锁,当继续向下执行method2时(因为被synchronized修饰,其锁同method1是同一个锁,切一直被t1线程锁拥有),根据锁重入性质,t1能直接执行method2,以此类推。如果synchronized不支持锁重入,就会造成死锁。我们知道method1方法中需要执行method2,也就是说method1必须执行完method2才能结束并释放这个对象锁,但是执行method2的前提是必须获得这个对象锁,这就造成了死锁。
父子类重入锁
package com.internet.thread;
public class SyncDubbo2 {
static class Main {
public int i = 10;
public synchronized void operationSup(){
try {
i--;
System.out.println("Main print i = "+i);
Thread.sleep(100);//休息0.1秒
} catch (Exception e) {
e.printStackTrace();
}
}
}
static class Sub extends Main{
public synchronized void operationSub(){
try {
while(i > 0){
i--;
System.out.println("Sub print i= "+i);
Thread.sleep(100);
this.operationSup();
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
public static void main(String[] args){
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
Sub sub = new Sub();
sub.operationSub();
}
});
t1.start();
}
}
执行结果
Sub print i= 9
Main print i = 8
Sub print i= 7
Main print i = 6
Sub print i= 5
Main print i = 4
Sub print i= 3
Main print i = 2
Sub print i= 1
Main print i = 0
每次执行sub的operationSub方法时,都需要执行main的operationSub方法,看运行结果可知,是按顺序执行,所以父子间也支持锁重入