(五)java多线程之Lock类

news/2024/2/29 2:35:51

本人邮箱: <kco1989@qq.com>
欢迎转载,转载请注明网址 http://blog.csdn.net/tianshi_kco
github: https://github.com/kco1989/kco
代码已经全部托管github有需要的同学自行下载

理论

java.util.concurrent.locks.Lock: Lock主要提供更多锁的特性让线程能获取同步方法或同步块的执行.它们提供更多的灵活的结果,能拥有不多的属性,并且可以配合Condition类提供多样的组合.
一个Lock是控制多线程去访问一个共享的资源.一般来说,一个lock会提供更高级的方法去访问共享资源:比如在某一个时间点,只能有一个线程获得lock,那么这个这个lock就能访问所有的共享资源.然而,有一种lock允许多个线程同时访问共享资源,这种lock就是读写锁ReadWriteLock.

一般使用synchronized的同步方法或同步块对每个对象提供的绝对的锁去访问它们, 但也是所有的锁的请求或释放都发生阻塞.当多线程访问共享资料,只要有一个锁获取了请求,那么其他锁则必须被释放掉.

synchronized的同步方法或同步块中,若使用监视锁会是编程更加简单.它已经帮助我们把在访问锁的时候屏蔽掉一下程序公共的错误.而且使我们能更加灵活用锁进行多线程操作.

一般lock的用法如下:

Lock l = ...;
l.lock();
try{}finally(l.unlock();
)

这是为了确保我们在使用的时候,如果执行方法出现异常,也能正常的释放锁.

编码

讲了那么多还是通过编码来学习感觉更加实际点.我们还是用之前那个售票系统

票类 Ticket

public class Ticket {private static final int DEFAULT_TICKET_COUNT = 1000;private int count = DEFAULT_TICKET_COUNT; //票的总数private int buyedCount = 0;private Object o = new Object();public  boolean buyTicket(int count) throws InterruptedException {if (this.count - count < 0){Thread.sleep(10);return false;}else{this.count = this.count - count;Thread.sleep(1);this.buyedCount = this.buyedCount + count;return true;}}public int getCount() {return count;}public int getBuyedCount() {return buyedCount;}public int getAllCount(){return count + buyedCount;}
}

然后再写一个售票类 TicketRunnable

public class TicketRunnable implements Runnable{private Ticket ticket;private Random random;private Lock lock;public TicketRunnable(Ticket ticket, Lock lock) {this.ticket = ticket;this.lock = lock;random = new Random();}@Overridepublic void run() {for (int i = 0; i < 5; i ++) {lock.lock();try {int count = random.nextInt(10) + 1;boolean success = ticket.buyTicket(count);System.out.println(String.format("%s打算买%d张票,买票%s了,还剩下%d张票,总共卖掉%d张票, 总票数%d",Thread.currentThread().getName(), count, success ? "成功" : "失败",ticket.getCount(), ticket.getBuyedCount(), ticket.getAllCount()));if (!success) {break;}}catch (Exception e){e.printStackTrace();}finally {lock.unlock();try {Thread.sleep(10);} catch (InterruptedException e) {e.printStackTrace();}}}}
}

然后写个程序测试一下

public class SyncThreadTest {public static void main(String[] args) throws InterruptedException {List<Thread> threads = new ArrayList<>();Ticket ticket = new Ticket();Lock lock = new ReentrantLock();for (int i = 0; i < 20; i ++){threads.add(new Thread(new TicketRunnable(ticket, lock)));}for (Thread thread : threads){thread.start();}}
}

运行一下程序,截取部分输出

Thread-7打算买8张票,买票成功了,还剩下654张票,总共卖掉346张票, 总票数1000
Thread-8打算买1张票,买票成功了,还剩下653张票,总共卖掉347张票, 总票数1000
Thread-9打算买5张票,买票成功了,还剩下648张票,总共卖掉352张票, 总票数1000
Thread-10打算买10张票,买票成功了,还剩下638张票,总共卖掉362张票, 总票数1000
Thread-11打算买3张票,买票成功了,还剩下635张票,总共卖掉365张票, 总票数1000
Thread-12打算买2张票,买票成功了,还剩下633张票,总共卖掉367张票, 总票数1000
Thread-13打算买10张票,买票成功了,还剩下623张票,总共卖掉377张票, 总票数1000
Thread-14打算买5张票,买票成功了,还剩下618张票,总共卖掉382张票, 总票数1000
Thread-15打算买10张票,买票成功了,还剩下608张票,总共卖掉392张票, 总票数1000
Thread-16打算买2张票,买票成功了,还剩下606张票,总共卖掉394张票, 总票数1000
Thread-17打算买2张票,买票成功了,还剩下604张票,总共卖掉396张票, 总票数1000
Thread-18打算买1张票,买票成功了,还剩下603张票,总共卖掉397张票, 总票数1000
Thread-19打算买6张票,买票成功了,还剩下597张票,总共卖掉403张票, 总票数1000
Thread-0打算买8张票,买票成功了,还剩下589张票,总共卖掉411张票, 总票数1000
Thread-1打算买2张票,买票成功了,还剩下587张票,总共卖掉413张票, 总票数1000
Thread-2打算买8张票,买票成功了,还剩下579张票,总共卖掉421张票, 总票数1000
Thread-3打算买5张票,买票成功了,还剩下574张票,总共卖掉426张票, 总票数1000
Thread-4打算买6张票,买票成功了,还剩下568张票,总共卖掉432张票, 总票数1000
Thread-5打算买1张票,买票成功了,还剩下567张票,总共卖掉433张票, 总票数1000
Thread-6打算买3张票,买票成功了,还剩下564张票,总共卖掉436张票, 总票数1000
Thread-7打算买1张票,买票成功了,还剩下563张票,总共卖掉437张票, 总票数1000
Thread-8打算买5张票,买票成功了,还剩下558张票,总共卖掉442张票, 总票数1000
Thread-9打算买8张票,买票成功了,还剩下550张票,总共卖掉450张票, 总票数1000
Thread-10打算买4张票,买票成功了,还剩下546张票,总共卖掉454张票, 总票数1000
Thread-11打算买5张票,买票成功了,还剩下541张票,总共卖掉459张票, 总票数1000
Thread-12打算买6张票,买票成功了,还剩下535张票,总共卖掉465张票, 总票数1000
Thread-13打算买1张票,买票成功了,还剩下534张票,总共卖掉466张票, 总票数1000
Thread-14打算买8张票,买票成功了,还剩下526张票,总共卖掉474张票, 总票数1000
Thread-15打算买2张票,买票成功了,还剩下524张票,总共卖掉476张票, 总票数1000
Thread-16打算买10张票,买票成功了,还剩下514张票,总共卖掉486张票, 总票数1000

发现运行结果都是正确的

现在尝试使用lock.tryLock()方法.不管能不能获得锁,每个线程都必须卖出5次票,修改TicketRunnable

public class TicketRunnable implements Runnable{private Ticket ticket;private Random random;private Lock lock;public TicketRunnable(Ticket ticket, Lock lock) {this.ticket = ticket;this.lock = lock;random = new Random();}@Overridepublic void run() {for (int i = 0; i < 5; ) {if(lock.tryLock()){try {int count = random.nextInt(10) + 1;boolean success = ticket.buyTicket(count);System.out.println(String.format("%s打算买%d张票,买票%s了,还剩下%d张票,总共卖掉%d张票, 总票数%d",Thread.currentThread().getName(), count, success ? "成功" : "失败",ticket.getCount(), ticket.getBuyedCount(), ticket.getAllCount()));if (!success) {break;}}catch (Exception e){e.printStackTrace();}finally {lock.unlock();i ++;}}else{System.out.println(Thread.currentThread().getName() + " 买票系统被占用,尝试获取锁失败.");try {Thread.sleep(10);} catch (InterruptedException e) {e.printStackTrace();}}}}
}

截取部分运行结果

Thread-11打算买7张票,买票成功了,还剩下613张票,总共卖掉387张票, 总票数1000
Thread-11打算买2张票,买票成功了,还剩下611张票,总共卖掉389张票, 总票数1000
Thread-11打算买7张票,买票成功了,还剩下604张票,总共卖掉396张票, 总票数1000
Thread-11打算买3张票,买票成功了,还剩下601张票,总共卖掉399张票, 总票数1000
Thread-11打算买4张票,买票成功了,还剩下597张票,总共卖掉403张票, 总票数1000
Thread-12 买票系统被占用,尝试获取锁失败.
Thread-10 买票系统被占用,尝试获取锁失败.
Thread-16 买票系统被占用,尝试获取锁失败.
Thread-19打算买2张票,买票成功了,还剩下595张票,总共卖掉405张票, 总票数1000
Thread-19打算买8张票,买票成功了,还剩下587张票,总共卖掉413张票, 总票数1000
Thread-19打算买1张票,买票成功了,还剩下586张票,总共卖掉414张票, 总票数1000
Thread-19打算买5张票,买票成功了,还剩下581张票,总共卖掉419张票, 总票数1000
Thread-19打算买1张票,买票成功了,还剩下580张票,总共卖掉420张票, 总票数1000

发现程序也是正常的.

原理

  • Lock.lock() 当前线程尝试获取一个锁,如果这个锁获取不到,则当前线程会一直休眠直到获取这个锁.

  • Lock.lockInterruptibly() 让当前线程获取一个锁,如果锁可以用,则直接返回.否则当前线程会一直休眠直到一下两种情况中的其中一个发生:

    • 当前线程获取到这个锁

    • 其他线程打断当前线程, 打断当前线程获取锁的操作是允许的.

  • Lock.tryLock() 尝试获得一个锁,如果锁是可用的,则直接返回ture,并获取到这个锁.否则,直接返回false

  • Lock.tryLock(long time, TimeUnit unit) 在某一段时间内尝试获取一个锁,如果锁可用,则直接返回true,否则等待超时返回fasle

  • Lock.unlock() 释放锁

  • Lock.newCondition() 这个后面的章节再谈论

打赏

如果觉得我的文章写的还过得去的话,有钱就捧个钱场,没钱给我捧个人场(帮我点赞或推荐一下)
微信打赏
支付宝打赏


https://www.jiucaihua.cn/news/show-1702354.html

相关文章

不在 sudoers 文件中。此事将被报告_油烟管道爬上小区外墙,有关部门将复查确定是否超标...

楚天都市报1月28日讯(记者刘闪 实习生方傲寒)今天&#xff0c;市民张先生反映&#xff0c;江汉区天梨豪园小区B栋二单元一家烧烤店将油烟管道安装在小区外墙上&#xff0c;他担心油烟污染和安全问题。张先生说&#xff0c;他的公司老板陈女士住在二单元12楼&#xff0c;现在不在…

java时间计算,秀出天际!

1 进程 进程与程序 操作系统之中最为通用的概念就是「进程」。与此相关的面试题以及各种技术优化策略也层出不穷&#xff0c;足以够证明它对于理解操作系统中举足轻重的地位。事实上&#xff0c;通过「进程」&#xff0c;程序员可以更为直观的理解自己所开发的软件&#xff0…

windows商店_笔记本amp;平板电脑最爱,Windows 应用商店里 实用的UWP应用程序 有哪些?...

自从 Windows8 以后&#xff0c;微软就在操作系统里面内置了「应用商店」。Windows 「应用商店」就是&#xff1a;各色最新 Windows应用软件的推荐平台统一的软件下载入口统一的软件购买窗口可以看得出&#xff0c;商店的的思路和 Apple App Store 是一样的。Windows10以前&…

oracle dbid 生成规则,Oracle恢复管理之重要参数DBID

**Oracle恢复管理之重要参数DBID**DBID&#xff0c;是数据库标识符。每个数据库都拥有一个编号&#xff0c;称为数据库标识符(DBID).该编号是在创建数据库时自动生成的&#xff0c;且Oracle不保证两个同名的数据库的DBID必然唯一的。所以Oracle提供$oracle_home/bin/nid命令&am…

java是由什么公司开发的,冲刺7天拿下Offer!

阿里巴巴Java岗面试题分享 1.HashMap 的内部结构&#xff1f;内部原理&#xff1f;和 HashTable 的区别&#xff0c;假如发⽣了 hash 碰撞&#xff0c;如何设计能让遍历效率⾼&#xff1f; 2.讲一讲讲讲 ConcurrentHashMap吧。 3.讲一下JVM虚拟机内存结构&#xff0c;以及它…

苹果手机没插耳机为什么显示耳机模式_手机投屏只投影像不投声音,声音还是从手机播出来...

手机投屏只投影像不投声音,声音还是从手机播出来用户有需求&#xff0c;AWIND奇机有方案&#xff0c;首次遇到手机投屏电视&#xff0c;希望声音保留才手机端的用户。其实这种方式也是可以的。用户需求如下&#xff1a;“手机投屏到电视&#xff0c;但是我看剧常常看到很晚&…

java更新未完成但是已安装,完整PDF

1、Java基础 Java基础务必要有一个非常牢固的根基&#xff0c;尤其是对于JVM和并发编程的掌握情况**&#xff08;属于进阶内容&#xff0c;但也是Java最为重要的基础内容&#xff09;**&#xff0c;不论是面试还是工作&#xff0c;基础不好&#xff0c;写不出高质量、漂亮的代…

R pdf大小_R语言绘图-基因表达矩阵热图

通常热图能够表达各个基因的表达量那么如何绘制基因表达矩阵热图呢&#xff1f;首先我们来看看基因表达矩阵表那么使用R语言进行绘制1.导入需要使用的包#install.packages("gplots") #install.packages("pheatmap") library(gplots) library(pheatmap)2.读…

跟小博老师一起学习数据库 ——ACID规则

2019独角兽企业重金招聘Python工程师标准>>> 现代的数据库基本都支持多用户的并发操作&#xff0c;为保证操作过程中数据的正确性&#xff0c;一定要包含原子性&#xff08;Atomicity&#xff09;、一致性&#xff08;Consistency&#xff09;、隔离性&#xff08;I…

java最好用的开发软件,掌握这些知识点再也不怕面试通不过!

1. 前言 大家都知道&#xff0c;Postman是一个非常受欢迎的API接口调试工具&#xff0c;提供有Chrome扩展插件版和独立的APP&#xff0c;不过它的很多高级功能都需要付费才能使用。 如果你连Postman都还没有用过&#xff0c;不妨可以先体验一番。 Postman官网&#xff1a; h…