Java多线程(3)——Lock-程序员宅基地

技术标签: ReentrantLock  java多线程  

以下是摘自JAVA API文档关于lock和synchronized的一段话:

  • Lock 实现提供了比使用 synchronized 方法和语句可获得的更广泛的锁定操作。此实现允许更灵活的结构,可以具有差别很大的属性,可以支持多个相关Condition对象。
  • synchronized 方法或语句的使用提供了对与每个对象相关的隐式监视器锁的访问,但却强制所有锁获取和释放均要出现在一个块结构中:当获取了多个锁时,它们必须以相反的顺序释放,且必须在与所有锁被获取时相同的词法范围内释放所有锁。
  • Lock 实现提供了使用 synchronized 方法和语句所没有的其他功能,包括提供了一个非块结构的获取锁尝试 (tryLock())、一个获取可中断锁的尝试 (lockInterruptibly()) 和一个获取超时失效锁的尝试 (tryLock(long, TimeUnit))。Lock 类还可以提供与隐式监视器锁完全不同的行为和语义,如保证排序、非重入用法或死锁检测。如果某个实现提供了这样特殊的语义,则该实现必须对这些语义加以记录。

先看一下Lock接口的定义的方法:

  •  void lock()  获取锁。 
  • void lockInterruptibly()   如果当前线程未被中断,则获取锁
  • Condition newCondition()  返回绑定到此 Lock 实例的新 Condition 实例
  • boolean tryLock()   仅在调用时锁为空闲状态才获取该锁
  • boolean tryLock(long time, TimeUnit unit)   如果锁在给定的等待时间内空闲,并且当前线程未被中断,则获取锁
  • void unlock()   释放锁

 注意:synchronized是在JVM层面上实现的,不但可以通过一些监控工具监控synchronized的锁定,而且在代码执行时出现异常,JVM会自动释放锁定,但是使用Lock则不行,lock是通过代码实现的,要保证锁定一定会被释放,就必须将unLock()放到finally{}中

Lock接口的实现类:ReentrantLock,   ReentrantReadWriteLock.ReadLock,   ReentrantReadWriteLock.WriteLock 。其中ReadLock和WriteLock是ReentrantReadWriteLock的内部类,ReentrantReadWriteLock是ReadWriteLock的实现类。关于ReadWriteLock后续再做详解。

ReentrantLock

一个可重入的互斥锁,它拥有与 synchronized 相同的并发性和内存语义,但是添加了类似轮询锁、定时锁等候和可中断锁等候的一些特性。构造方法:

  • ReentrantLock()  创建一个 ReentrantLock 的实例 
  • ReentrantLock(boolean fair)   创建一个具有给定公平策略的 ReentrantLock----公平锁

ReentrantLock特有的方法:

  • int getHoldCount()  查询当前线程保持此锁的次数。 
  • int getQueueLength()  返回正等待获取此锁的线程估计数
  • int getWaitQueueLength(Condition condition)  返回等待与此锁相关的给定条件的线程估计数
  • boolean hasQueuedThread(Thread thread)   查询给定线程是否正在等待获取此锁
  • boolean hasQueuedThreads()  查询是否有些线程正在等待获取此锁
  • boolean hasWaiters(Condition condition)  查询是否有些线程正在等待与此锁有关的给定条件
  • boolean isFair()  如果此锁的公平设置为 true,则返回 true
  • boolean isHeldByCurrentThread()  查询当前线程是否保持此锁
  • boolean isLocked()  查询此锁是否由任意线程保持

如下实现两个线程轮询处理公共数据:

public class ReenLockTest {
	
	public static void main(String[] args) {
		new Thread(new Runnable(){
			@Override
			public void run() {
				Thread1.m1();			
			}			
		},"thread1").start();
		new Thread(new Runnable(){
			@Override
			public void run() {
				Thread1.m2();			
			}			
		},"thread2").start();
		
	}
	static ReentrantLock lock = new ReentrantLock();
	static ReentrantLock lock2 = new ReentrantLock();
	static int i = 100;
	static class Thread1{
		public static void m1(){
			while(i>0){
				lock.lock();
				try {				
					System.out.println("method m1()" + i--);					
				} catch (Exception e) {
					e.printStackTrace();
				}finally{
					if(!lock2.isLocked())
						lock2.unlock();
				}
			}
		}
		
		public static void m2(){
			while(i>0){
				lock2.lock();
				try {
					System.out.println("method m2()" + i--);					
				} catch (Exception e) {
					e.printStackTrace();
				}finally{
					if(!lock.isLocked())
						lock.unlock();
				}
			}
		}		
	}
}

重入锁:

ReentrantLock锁,可以被单个线程多次获取:

ReentrantLock部分方法介绍:

1、tryLock():

  • boolean tryLock()   尝试获取锁,仅在调用时锁为空闲状态才获取该锁。
  • boolean tryLock(long timeout, TimeUnit unit)  如果锁在给定等待时间内没有被另一个线程保持,且当前线程未被中断,则获取该锁。 

如果该锁没有被另一个线程保持,并且立即返回 true 值,则将锁的保持计数设置为 1。即使已将此锁设置为使用公平排序策略,但是调用 tryLock() 立即获取锁(如果有可用的),而不管其他线程当前是否正在等待该锁。

  • 如果当前线程已经保持此锁,则将保持计数加 1,该方法将返回 true
  • 如果锁被另一个线程保持,则此方法将立即返回 false 值。

tryLock()的用法与lock()的使用方式基本相同,只是在释放锁时,tryLock()方式获取的锁需要先验证当前线程是否获取到锁isLocked()

static class Thread1{
		public static void m1(){
			while(i>0){
				boolean tryLock = lock.tryLock();
				if(tryLock){
					try {	
						Thread.sleep(500);
						System.out.println("method m1()" + i--);					
					} catch (Exception e) {
						e.printStackTrace();
					}finally{
						if(lock.isLocked())
							lock.unlock();
					}
				}
			}
		}

2、lockInterruptibly()  

  • void lockInterruptibly()  如果当前线程未被中断,则获取锁

  1. 如果该锁没有被另一个线程保持,则获取该锁并立即返回,将锁的保持计数设置为 1。 
  2. 如果当前线程已经保持此锁,则将保持计数加 1,并且该方法立即返回。
  3. 如果锁被另一个线程保持,则出于线程调度目的,禁用当前线程,并且在发生以下两种情况之一以前,该线程将一直处于休眠状态: 
                                   锁由当前线程获得;
                                  其他某个线程中断当前线程。
  4. 如果当前线程获得该锁,则将锁保持计数设置为 1。 
  5. 如果当前线程在进入此方法时已经设置了该线程的中断状态;或者 在等待获取锁的同时被中断。 则抛出 InterruptedException,并且清除当前线程的已中断状态。 

lockInterruptibly() 与lock() 的区别:

  • lock 优先考虑获取锁,待获取锁成功后,才响应中断。使用lock()方法获取锁标记,如果需要阻塞等待锁标记,不能被打断
  • lockInterruptibly 优先考虑响应中断,而不是响应锁的普通获取或重入获取。使用lockInterribly()方法获取锁标记,如果需要阻塞等待锁标记,可以被打断。
public class TryLockTest {
	static Lock lock = new ReentrantLock();
	static Lock lock2 = new ReentrantLock();
	
	public static void main(String[] args) {
		Thread t1 = new Thread(new Runnable(){
			@Override
			public void run() {
				m1();
			}			
		});
		
		Thread t2 = new Thread(new Runnable(){
			@Override
			public void run() {
				m2();
			}			
		});
		t1.start();
		t2.start();
		t1.interrupt();
		t2.interrupt();
		
	}
	
	public static void m1(){
		lock.lock();
		for(int i=0 ;i<20 ; i++){
			try {
				Thread.sleep(500);
			} catch (InterruptedException e) {
				System.out.println("m1() java.lang.InterruptedException");
			}
			System.out.println("m1 *running");
		}
		lock.unlock();
		
	}
	
	public static void m2(){
		try {
			lock2.lockInterruptibly();
			for(int i=0 ;i<20 ; i++){			
				Thread.sleep(500);			
				System.out.println("m2 **running");
			}
		} catch (InterruptedException e) {
			System.out.println("m2() java.lang.InterruptedException");
		}finally{
			if(((ReentrantLock) lock2).isLocked())
				lock2.unlock();
		}
		
	}
}

输出结果:

从输出结果中可以看到,m1()执行,而m2()被打断。

注意:

  • interrupt()方法打断线程休眠状态,会抛出异常  InterruptedException。
  • m1()方法的被打断一次,是因为在调用sleep()方法进入休眠状态时被打断一次。

关于线程中断请看这篇博客:Java并发之线程中断
 

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/zhoushimiao1990/article/details/89237362

智能推荐

Dubbo,ZooKeeper,Redis,FastDFS,ActiveMQ,Keepalived,Nginx,Hudson_电商 dubbo zookeeper activemq fastdfs redis eginx 架构-程序员宅基地

文章浏览阅读3.9k次。获取【下载地址】 QQ: 313596790 【免费支持更新】三大数据库mysql oracle sqlsever 更专业、更强悍、适合不同用户群体【新录针对本系统的视频教程,手把手教开发一个模块,快速掌握本系统】A 集成代码生成器(开发利器)+快速构建表单; QQ:313596790freemaker模版技术 ,0个代码不用写,生成完_电商 dubbo zookeeper activemq fastdfs redis eginx 架构

凸优化笔记10(罚函数法-内点罚函数、外点罚函数)_罚函数法例题讲解-程序员宅基地

文章浏览阅读5k次,点赞6次,收藏41次。下面将介绍罚函数法,并且对等式约束不等式约束适用内点法还是外点法做了说明,与如何选取罚函数。并且给与相应例题。罚函数的基本思想是,借助罚函数把约束问题转化为无约束问题,进而用无约束最优化方法求解。提示:这里对文章进行总结:例如:以上就是今天要讲的内容,本文仅仅简单介绍了pandas的使用,而pandas提供了大量能使我们快速便捷地处理数据的函数和方法。_罚函数法例题讲解

SAP CRM 将组件整合至导航栏中_sap crm如何将组件添加到导航-程序员宅基地

文章浏览阅读1.2k次。到现在,我们已经可以让组件独立地显示。我们只是运行它、让它显示在Web UI中。让我们把组件整合进导航栏,使我们可以在正常登录Web UI时访问它。 步骤一:  为你的UI组件主窗体创建一个内向插件。 步骤二:  将你的组件的主窗体在运行时资源库中作为一个界面视图暴露出来(Interface View)。 步骤三:_sap crm如何将组件添加到导航

1.简述GPS载波相位测量的基本原理。2.在高精度GPS测量工作中,为什么需要采用载波相位测量方法进行三维定位。3.载波相位测量中,确定整周未知数主要有哪些方法?GNSS测量与数据处理(第六周作业)_载波相位测量的相位csdn-程序员宅基地

文章浏览阅读8.5k次,点赞9次,收藏34次。1.简述GPS载波相位测量的基本原理?载波相位测量的观测量是GPS接收机所接收的卫星载波信号与接收机本振参考信号的相位差。以#[(4)表示k接收机在接收机钟面时刻4%时所接收到的j卫星载波信号的相位值,φ。(t)表示ht接收机在钟面时刻i时所产生的本地参考信号的相位值,则k接收机在接收机钟面时刻t时观测j卫星所取得的相位观测量可写为中(tn) =中o(tr) - p’(5n)通常的相位或相位..._载波相位测量的相位csdn

Linux安装JDK,Nginx_ngnix安装后需要安装jdk-程序员宅基地

文章浏览阅读265次。如何在Linux下安装JDK和Nginx?Linux下安装JDK把 JDK 的安装包上传到服务器,目录如下 /user/local/java然后我们需要解压安装包解压 tar zxvf jdk-8u301-linux-x64.tar.gz解压完成后,JDK 的安装目录就出现了,复制目录路径 /usr/local/java/jdk1.8.0_301我们安装完 JDK 之后,是不是还需要配置环境变量让 JDK 生效,接下来就是配置环境变量的操作了。3.配置环境变量vim /etc/pr_ngnix安装后需要安装jdk

vijos 1221 神秘配方 题解_配方编程c++-程序员宅基地

文章浏览阅读243次。题面背景每天中午的下课铃一响,浙江镇海中学(什么?镇海中学)的同学们都会冲出学校来附近的小饭馆吃饭,刹那间天昏地暗,飞砂走石,家家餐馆内都是一片黑压压的人 。馄饨店、饺子馆,在学校附近开一家红一家。身为镇海中学信息中心首席科学顾问兼资深信息学竞赛辅导老师Dennis看到了,他为了在业余时间方便学生,他租了学校附近的一家店面,雇了几个拉面师傅,开了一家“正宗兰州牛肉拉面馆”,生意还不错。..._配方编程c++

随便推点

linux下查看端口占用情况、查看所有tcp端口情况_linux 机器如何查看大量处于tcp_wait 的端口是哪个-程序员宅基地

文章浏览阅读2w次,点赞3次,收藏17次。1、linux下查看所有占用端口情况netstat -ntlp2、查看所有某个端口使用情况,如80端口。netstat -ntulp |grep 803、查看一台服务器上面哪些服务及端口。netstat -lanp4、查看一个服务有几个端口,比如要查看mysqld。ps -ef |grep mysqldnetstat命令各个参数说明如下:-a..._linux 机器如何查看大量处于tcp_wait 的端口是哪个

Linux后门系列--由浅入深sk13完全分析(缩水版)-程序员宅基地

文章浏览阅读68次。创建时间:2007-09-11文章属性:原创文章提交:wzt (wzt_at_xsec.org)Linux后门系列--由浅入深sk13完全分析(缩水版)作者 wzt联系方式 [email protected]个人网站 http://tthacker.cublog.cn http://xsec.org本文首发《***防线》第八期,本文在《黑防》里有更详细更细致..._get_sct()

swarm操作命令_修改了swarm-base.yml文件,怎么重启-程序员宅基地

文章浏览阅读823次。docker config # 查看已创建配置文件 - docker config ls # 将已有配置文件添加到docker配置文件中 - docker config create docker 配置文件名 本地配置文件 docker node # 查看集群中的节点 - docker node ls # 将m..._修改了swarm-base.yml文件,怎么重启

[HAOI2011]Problem b_[haoi2011] 问题b-程序员宅基地

文章浏览阅读422次。[HAOI2011]Problem bDescription对于给出的n个询问,每次求有多少个数对(x,y),满足a≤x≤b,c≤y≤d,且gcd(x,y) = k,gcd(x,y)函数为x和y的最大公约数。Input第一行一个整数n,接下来n行每行五个整数,分别表示a、b、c、d、k100%的数据满足:1≤n≤50000,1≤a≤b≤50000_[haoi2011] 问题b

第27天 | 28天学会PyQt5,高级控件之QListWidget_pythonlist widget用法-程序员宅基地

文章浏览阅读1.4k次。列表框架还有一个QListWidget,它是QListView的子类,与QListView不同的是,它已经建立了一个数据储存模型QListWidgetItem,直接调用addItem()函数就可以添加条目(Item),常用的事件类型有: 事件类型 描述 currentItemChanged 列表中的条目发生改变时,触发此事件; itemClicked 点击列表中的条目时,触发此事件。 _pythonlist widget用法

Longformer详解-程序员宅基地

文章浏览阅读8.4k次,点赞11次,收藏43次。IntroductionLongformer是一种可高效处理长文本的模型,出自AllenAI 2020年4月10日。目前已经开源,而且可以通过huggingface快速使用传统Transformer-based模型在处理长文本时存在一些问题,因为它们均采用"我全都要看"型的attention机制,即每一个token都要与其他所有token进行交互,无论是空间还是时间复杂度都高达O(n2)O(n^2)O(n2)。为了解决这个问题,之前有些工作是将长文本切分为若干个较短的Text Segment,然后逐个处_longformer