java多线程之Lock锁原理以及案例实现电影院卖票

2023-10-06 15:50

本文主要是介绍java多线程之Lock锁原理以及案例实现电影院卖票,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

为什么会出现Lock锁?

我们知道 synchronized 给代码加锁或解锁时,我们并没有直接看到在哪里加上了锁,在哪里释放了锁,为了更清晰的表达如何加锁和释放锁,JDK5以后提供了一个新的锁对象Lock

Lock锁使用

Lock实现提供比使用synchronized方法和语句可以获得更广泛的锁定操作
Lock中提供了获得锁和释放锁的方法:

  • void lock();//获得锁
  • void unlock();//释放锁
  • Lock类是接口,不能直接实例化,这里采用他的实现类ReentrantLock来实例化
    ReentrantLock的构造方法
    ReentrantLock():创建一个ReentrantLock的实例

代码实战

需求:某电影院目前正在上映国产大片,共有100张票,而它有3个窗口卖票,请设计一个程序模拟该电影院卖票

package com.heima.thread001;import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;public class MyRun implements Runnable {int ticket = 0;Lock lock = new ReentrantLock();@Overridepublic void run() {while (true){lock.lock();try {if (ticket == 100) {break;}else {ticket ++;System.out.println(Thread.currentThread().getName() + "正在卖第" + ticket + "张票 ...");}} catch (Exception e) {e.printStackTrace();} finally {lock.unlock();}}}
}
package com.heima.thread001;import java.util.concurrent.ExecutionException;public class TestDemo {public static void main(String[] args) throws ExecutionException, InterruptedException {MyRun myRun = new MyRun();//创建线程对象Thread t1 = new Thread(myRun);Thread t2 = new Thread(myRun);Thread t3 = new Thread(myRun);//起名字t1.setName("窗口1");t2.setName("窗口2");t3.setName("窗口3");//开启线程t1.start();t2.start();t3.start();}
}

运行结果如下:

窗口1正在卖第1张票 ...
窗口3正在卖第2张票 ...
窗口3正在卖第3张票 ...
窗口3正在卖第4张票 ...
窗口3正在卖第5张票 ...
窗口3正在卖第6张票 ...
窗口3正在卖第7张票 ...
窗口3正在卖第8张票 ...
窗口3正在卖第9张票 ...
窗口3正在卖第10张票 ...
窗口3正在卖第11张票 ...
窗口3正在卖第12张票 ...
窗口3正在卖第13张票 ...
窗口3正在卖第14张票 ...
窗口3正在卖第15张票 ...
窗口3正在卖第16张票 ...
窗口3正在卖第17张票 ...
窗口3正在卖第18张票 ...
窗口3正在卖第19张票 ...
窗口3正在卖第20张票 ...
窗口3正在卖第21张票 ...
窗口3正在卖第22张票 ...
窗口3正在卖第23张票 ...
窗口3正在卖第24张票 ...
窗口3正在卖第25张票 ...
窗口3正在卖第26张票 ...
窗口3正在卖第27张票 ...
窗口3正在卖第28张票 ...
窗口3正在卖第29张票 ...
窗口3正在卖第30张票 ...
窗口3正在卖第31张票 ...
窗口3正在卖第32张票 ...
窗口3正在卖第33张票 ...
窗口3正在卖第34张票 ...
窗口3正在卖第35张票 ...
窗口3正在卖第36张票 ...
窗口3正在卖第37张票 ...
窗口3正在卖第38张票 ...
窗口3正在卖第39张票 ...
窗口3正在卖第40张票 ...
窗口3正在卖第41张票 ...
窗口3正在卖第42张票 ...
窗口3正在卖第43张票 ...
窗口3正在卖第44张票 ...
窗口3正在卖第45张票 ...
窗口3正在卖第46张票 ...
窗口3正在卖第47张票 ...
窗口3正在卖第48张票 ...
窗口3正在卖第49张票 ...
窗口3正在卖第50张票 ...
窗口3正在卖第51张票 ...
窗口3正在卖第52张票 ...
窗口3正在卖第53张票 ...
窗口3正在卖第54张票 ...
窗口3正在卖第55张票 ...
窗口3正在卖第56张票 ...
窗口3正在卖第57张票 ...
窗口3正在卖第58张票 ...
窗口3正在卖第59张票 ...
窗口3正在卖第60张票 ...
窗口3正在卖第61张票 ...
窗口3正在卖第62张票 ...
窗口3正在卖第63张票 ...
窗口3正在卖第64张票 ...
窗口3正在卖第65张票 ...
窗口3正在卖第66张票 ...
窗口3正在卖第67张票 ...
窗口3正在卖第68张票 ...
窗口3正在卖第69张票 ...
窗口3正在卖第70张票 ...
窗口3正在卖第71张票 ...
窗口3正在卖第72张票 ...
窗口3正在卖第73张票 ...
窗口3正在卖第74张票 ...
窗口3正在卖第75张票 ...
窗口3正在卖第76张票 ...
窗口3正在卖第77张票 ...
窗口3正在卖第78张票 ...
窗口3正在卖第79张票 ...
窗口3正在卖第80张票 ...
窗口3正在卖第81张票 ...
窗口3正在卖第82张票 ...
窗口3正在卖第83张票 ...
窗口3正在卖第84张票 ...
窗口3正在卖第85张票 ...
窗口3正在卖第86张票 ...
窗口3正在卖第87张票 ...
窗口3正在卖第88张票 ...
窗口3正在卖第89张票 ...
窗口3正在卖第90张票 ...
窗口3正在卖第91张票 ...
窗口3正在卖第92张票 ...
窗口3正在卖第93张票 ...
窗口3正在卖第94张票 ...
窗口3正在卖第95张票 ...
窗口3正在卖第96张票 ...
窗口3正在卖第97张票 ...
窗口3正在卖第98张票 ...
窗口3正在卖第99张票 ...
窗口3正在卖第100张票 ...

知识扩展

1、synchronized和Lock的区别?

  • synchronized是关键字,Lock是接口。
  • synchronized无法获取锁的状态,Lock可以。
  • synchronized会自动释放锁,Lock需要手动。
  • synchronized没有Lock锁灵活(Lock锁可以自己定制)。

2、ReentrantLock 和 Synchronized 对比

  • ReentrantLock 通过 lock() 和 unlock() 加解锁,Synchronized会自动解锁,ReentrantLock需要手动解锁
  • ReentrantLock相比Synchronized的优势是: 可中断,公平锁,多个锁

3、Lock 接口的主要方法

  • void lock() — 如果lock是空闲的,当前线程就会获取到锁
  • boolean tryLock() — 如果lock可用,则获取锁并且返回true,否则返回false
  • void unlock() — 当前线程释放持有的锁
  • Condition newCondition() — 条件对象: 获取等待通知组件
  • getHoldCount() — 查询当前线程保持此锁的次数
  • getQueueLength() — 返回正在等待此锁的线程数量
  • getWaitQueueLength() — 返回正在等待与此锁相关给定条件的线程数量
  • hasWaiters(Condition condition) — 查询在特定Condition下是否有线程等待
  • hasQueuedThread(Thread thread) — 查询给定线程是否等待获取此锁
  • hasQueuedThreads — 是否有线程等待此锁
  • isFair() — 该锁是否是公平锁
  • isHeldByCurrentThread() — 当前线程是否保持锁定
  • isLock() — 此锁是否有任意线程占用
  • lockInterruptibly() — 如果当前线程未被中断,获取锁
  • tryLock() — 尝试获取锁,仅在调用时锁未被线程占用,获得锁
  • tryLock(long timeout, TimeUnit unit) — 如果锁在给定等待时间没有被另一个线程保持,获取锁

锁优化

  • 减少锁持有的时间 — 只在有线程安全要求的程序上加锁
  • 减少锁粒度 — 将大的对象拆成小对象,增加并行度,减少锁竞争
  • 锁分离 — 读写锁的思想,读的时候加read lock, 写的时候加 write lock
  • 锁粗化 — 保证线程持有锁的时间尽量短
  • 锁消除 — 如果发现不能被共享的对象,需要消除这些对象的锁操作

这篇关于java多线程之Lock锁原理以及案例实现电影院卖票的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/153429

相关文章

Java实现字节字符转bcd编码

《Java实现字节字符转bcd编码》BCD是一种将十进制数字编码为二进制的表示方式,常用于数字显示和存储,本文将介绍如何在Java中实现字节字符转BCD码的过程,需要的小伙伴可以了解下... 目录前言BCD码是什么Java实现字节转bcd编码方法补充总结前言BCD码(Binary-Coded Decima

SpringBoot全局域名替换的实现

《SpringBoot全局域名替换的实现》本文主要介绍了SpringBoot全局域名替换的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一... 目录 项目结构⚙️ 配置文件application.yml️ 配置类AppProperties.Ja

Java使用Javassist动态生成HelloWorld类

《Java使用Javassist动态生成HelloWorld类》Javassist是一个非常强大的字节码操作和定义库,它允许开发者在运行时创建新的类或者修改现有的类,本文将简单介绍如何使用Javass... 目录1. Javassist简介2. 环境准备3. 动态生成HelloWorld类3.1 创建CtC

JavaScript中的高级调试方法全攻略指南

《JavaScript中的高级调试方法全攻略指南》什么是高级JavaScript调试技巧,它比console.log有何优势,如何使用断点调试定位问题,通过本文,我们将深入解答这些问题,带您从理论到实... 目录观点与案例结合观点1观点2观点3观点4观点5高级调试技巧详解实战案例断点调试:定位变量错误性能分

Python实现批量CSV转Excel的高性能处理方案

《Python实现批量CSV转Excel的高性能处理方案》在日常办公中,我们经常需要将CSV格式的数据转换为Excel文件,本文将介绍一个基于Python的高性能解决方案,感兴趣的小伙伴可以跟随小编一... 目录一、场景需求二、技术方案三、核心代码四、批量处理方案五、性能优化六、使用示例完整代码七、小结一、

Java实现将HTML文件与字符串转换为图片

《Java实现将HTML文件与字符串转换为图片》在Java开发中,我们经常会遇到将HTML内容转换为图片的需求,本文小编就来和大家详细讲讲如何使用FreeSpire.DocforJava库来实现这一功... 目录前言核心实现:html 转图片完整代码场景 1:转换本地 HTML 文件为图片场景 2:转换 H

Java使用jar命令配置服务器端口的完整指南

《Java使用jar命令配置服务器端口的完整指南》本文将详细介绍如何使用java-jar命令启动应用,并重点讲解如何配置服务器端口,同时提供一个实用的Web工具来简化这一过程,希望对大家有所帮助... 目录1. Java Jar文件简介1.1 什么是Jar文件1.2 创建可执行Jar文件2. 使用java

C#使用Spire.Doc for .NET实现HTML转Word的高效方案

《C#使用Spire.Docfor.NET实现HTML转Word的高效方案》在Web开发中,HTML内容的生成与处理是高频需求,然而,当用户需要将HTML页面或动态生成的HTML字符串转换为Wor... 目录引言一、html转Word的典型场景与挑战二、用 Spire.Doc 实现 HTML 转 Word1

C#实现一键批量合并PDF文档

《C#实现一键批量合并PDF文档》这篇文章主要为大家详细介绍了如何使用C#实现一键批量合并PDF文档功能,文中的示例代码简洁易懂,感兴趣的小伙伴可以跟随小编一起学习一下... 目录前言效果展示功能实现1、添加文件2、文件分组(书签)3、定义页码范围4、自定义显示5、定义页面尺寸6、PDF批量合并7、其他方法

SpringBoot实现不同接口指定上传文件大小的具体步骤

《SpringBoot实现不同接口指定上传文件大小的具体步骤》:本文主要介绍在SpringBoot中通过自定义注解、AOP拦截和配置文件实现不同接口上传文件大小限制的方法,强调需设置全局阈值远大于... 目录一  springboot实现不同接口指定文件大小1.1 思路说明1.2 工程启动说明二 具体实施2