第三章:java线程重入锁学习「建议收藏」

第三章:java线程重入锁学习「建议收藏」第三章: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修饰,说明父子类的情况锁重入也是可以的。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/106149.html原文链接:https://javaforall.net

赞 (0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • 云服务器 Amazon Aws 的峰值带宽是多少 mbps?

    云服务器 Amazon Aws 的峰值带宽是多少 mbps?

    2022年2月13日
  • .Net Core HttpClient处理响应压缩「建议收藏」

    .Net Core HttpClient处理响应压缩「建议收藏」前言在上篇文章[ASP.NETCore中的响应压缩]中我们谈到了在ASP.NETCore服务端处理关于响应压缩的请求,服务端的主要工作就是根据Content-Encoding头信息判断采用哪种方式压缩并返回。之前在群里有人问道过,现在的网络带宽这么高了还有必要在服务端针对请求进行压缩吗?确实,如今分布式和负载均衡技术这么成熟,很多需要处理高并发大数据的场景都可以通过增加服务器节点来进行。但是,在资源受限的情况下,或者是还没必要为了某一个点去增加新的服务器节点的时候,我们还是要采用一些程序本身的常规处理

    2022年7月15日
  • 在Windows XP使用LC5(L0phtCrack)[通俗易懂]

    在Windows XP使用LC5(L0phtCrack)[通俗易懂]在主机里新建用户名,密码陆续为空密码,与用户名相同密码,password123,分别进行登陆测试;

    2022年7月24日
  • Hi3516A开发–视频接口[通俗易懂]

    Hi3516A开发–视频接口[通俗易懂]参看:几种常用的视频接口我们经常在家里的电视机、各种播放器上,视频会议产品和监控产品的编解码器的视频输入输出接口上看到很多视频接口,这些视频接口哪些是模拟接口、哪些是数字接口,哪些接口可以传输高清图像等,下面就做一个详细的介绍。  目前最基本的视频接口是复合视频接口、S-vidio接口;另外常见的还有色差接口、VGA接口、接口、HDMI接口、SDI接口。  1、复合

    2022年5月15日
  • 液压油报关_液压油算是危险品吗

    液压油报关_液压油算是危险品吗  韩国液压油进口代理报关知识整理  液压油就是我们利用液体压力能的液压控制系统可以使用的液压介质,在液压技术系统中起着能量信息传递、抗磨、系统进行润滑、防腐、防锈、冷却等作用。对于液压油,首先要满足液压装置在工作温度和启动温度下的液体粘度要求,因为润滑油的粘度变化直接关系到液压作用、传动效率和传动精度,还要求润滑油的粘温性能和剪切稳定性能满足不同用途的需要。液压油的种类繁多,分类管理方法各异,长期发展以来,习惯以用途可以进行垃圾分类,也有学生根据不同油品类型、化学组分或可燃性分类的。这些分类方法只反

    2022年9月21日

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号