第三章:java线程重入锁学习

重入锁,顾名思义,就是支持重进入的锁,它表示该锁能够支持一个线程对资源的重复加锁。重进入是指任意线程在获取到锁之后能够再次获取该锁而不会被锁阻塞,该特性的实现需要解决以下两个问题。

  • 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修饰,说明父子类的情况锁重入也是可以的。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

老马识途2.0

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值