02_Lock锁

首先看一下JUC的重磅武器——锁(Lock)

相比同步锁,JUC包中的Lock锁的功能更加强大,它提供了各种各样的锁(公平锁,非公平锁,共享锁,独占锁……),所以使用起来很灵活。

翻译过来就是:

锁实现提供了比使用同步方法和语句可以获得的更广泛的锁操作。它们允许更灵活的结构,可能具有非常不同的属性,并且可能支持多个关联的条件对象。

Lock是一个接口,这里主要有三个实现:

  • ReentrantLock
  • ReentrantReadWriteLock.ReadLock
  • ReentrantReadWriteLock.WriteLock

一、ReentrantLock可重入锁(递归锁) 

使用ReentrantLock改造卖票程序:只需改造sale()方法

private ReentrantLock lock = new ReentrantLock();  //创建实例对象

lock.lock(); //加锁

lock.unlock(); //释放锁

class Ticket{private Integer number = 20;private ReentrantLock lock = new ReentrantLock();public void sale(){lock.lock();if (number <= 0) {System.out.println("票已售罄!");lock.unlock();return;}try {Thread.sleep(200);number--;System.out.println(Thread.currentThread().getName() + "买票成功,当前剩余:" + number);} catch (InterruptedException e) {e.printStackTrace();} finally {lock.unlock();}}
}

1. 测试可重入性

可重入锁又名递归锁,是指在同一个线程在外层方法获取锁的时候,再进入该线程的内层方法会自动获取锁。Java中ReentrantLock和synchronized都是可重入锁可重入锁的一个优点是可一定程度避免死锁。

例如下列伪代码:

class A{public synchronized void aa{......bb();......}public synchronized void bb{......}
}
A a = new A();
a.aa();

A类中有两个普通同步方法,都需要对象a的锁。如果是不可重入锁的话,aa方法首先获取到锁,aa方法在执行的过程中需要调用bb方法,此时锁被aa方法占有,bb方法无法获取到锁,这样就会导致bb方法无法执行,aa方法也无法执行,出现了死锁情况。可重入锁可避免这种死锁的发生。

class Ticket{private Integer number = 20;private ReentrantLock lock = new ReentrantLock();public void sale(){lock.lock();if (number <= 0) {System.out.println("票已售罄!");lock.unlock();return;}try {Thread.sleep(200);number--;System.out.println(Thread.currentThread().getName() + "买票成功,当前剩余:" + number);// 调用check方法测试锁的可重入性this.check();} catch (InterruptedException e) {e.printStackTrace();} finally {lock.unlock();}}/*** 为了测试可重入锁,添加检查余票方法*/public void check(){lock.lock();System.out.println("检查余票。。。。");lock.unlock();}
}

可以发现程序可以正常执行。。。说明该锁确实可重入。

AAA买票成功,当前剩余:19
检查余票。。。。
AAA买票成功,当前剩余:18
检查余票。。。。
AAA买票成功,当前剩余:17
检查余票。。。。
AAA买票成功,当前剩余:16
检查余票。。。。
AAA买票成功,当前剩余:15
检查余票。。。。
AAA买票成功,当前剩余:14
检查余票。。。。
AAA买票成功,当前剩余:13
检查余票。。。。
BBB买票成功,当前剩余:12
检查余票。。。。
BBB买票成功,当前剩余:11
检查余票。。。。
BBB买票成功,当前剩余:10
。。。。。。

2. 测试公平锁

ReentrantLock还可以实现公平锁。所谓公平锁,也就是在锁上等待时间最长的线程将获得锁的使用权。通俗的理解就是谁排队时间最长谁先执行获取锁。

ReentrantLock lock = new ReentrantLock();         //默认非公平锁
ReentrantLock lock = new ReentrantLock(true);     //true表示创建公平锁

    //默认非公平锁//ReentrantLock lock = new ReentrantLock();//true表示创建公平锁ReentrantLock lock = new ReentrantLock(true);public void test() throws InterruptedException {//lock.tryLock():获取锁并立即返货获取锁的结果,成功返回true,失败false
//        lock.tryLock(timeout,timeunit): 最多阻塞等待timeout单位timeunit 时间,获取成功返回true,失败falseif(lock.tryLock(6, TimeUnit.SECONDS)){//获取锁成功System.out.println(Thread.currentThread().getName()+"开始执行...");Thread.sleep(200);System.out.println(Thread.currentThread().getName()+"执行结束...");lock.unlock();}else{System.out.println(Thread.currentThread().getName()+"获取锁失败....");}}
}

测试结果:可以看到ABC三个线程是按顺序买票成功的。

AAA买票成功,当前剩余:19
检查余票。。。。
BBB买票成功,当前剩余:18
检查余票。。。。
CCC买票成功,当前剩余:17
检查余票。。。。
AAA买票成功,当前剩余:16
检查余票。。。。
BBB买票成功,当前剩余:15
检查余票。。。。
CCC买票成功,当前剩余:14
。。。。。。

3. 限时等待

这个是什么意思呢?也就是通过我们的tryLock方法来实现,可以选择传入时间参数,表示等待指定的时间,无参则表示立即返回锁申请的结果:true表示获取锁成功,false表示获取锁失败。我们可以将这种方法用来解决死锁问题

lock.tryLock():获取锁并立即返货获取锁的结果,成功返回true,失败false
lock.tryLock(timeout,timeunit): 最多阻塞等待timeout单位timeunit 时间,获取成功返回true,失败false
 

public class Demo3 {public static void main(String[] args) {Demo3 demo3 = new Demo3();new Thread(()->{demo3.test();},"A").start();new Thread(()->{demo3.test();},"B").start();}Lock lock = new ReentrantLock();public void test(){try {boolean b = lock.tryLock(1000, TimeUnit.MILLISECONDS);if(!b){System.out.println(Thread.currentThread().getName()+"获取锁失败");return;}Thread.sleep(2000);System.out.println(Thread.currentThread().getName()+"....");lock.unlock();} catch (InterruptedException e) {e.printStackTrace();lock.unlock();}}
}

4. ReentrantLock和synchronized区别

(1)synchronized是独占锁,加锁和解锁的过程自动进行,易于操作,但不够灵活。ReentrantLock也是独占锁,加锁和解锁的过程需要手动进行,不易操作,但非常灵活。

(2)synchronized可重入,因为加锁和解锁自动进行,不必担心最后是否释放锁;ReentrantLock也可重入,但加锁和解锁需要手动进行,且次数需一样,否则其他线程无法获得锁。

(3)synchronized不可响应中断,一个线程获取不到锁就一直等着;ReentrantLock可以响应中断。

(4)synchronzied锁的是对象,锁是保存在对象头里面的,根据对象头数据来标识是否有线程获得锁/争抢锁;ReentrantLock锁的是线程,根据进入的线程和int类型的state标识锁的获得/争抢。

二、ReentrantReadWriteLock读写锁

在并发场景中用于解决线程安全的问题,我们几乎会高频率的使用到独占式锁,通常使用java提供的关键字synchronized或者concurrents包中实现了Lock接口的ReentrantLock。它们都是独占式获取锁,也就是在同一时刻只有一个线程能够获取锁

现实中有这样一种场景:对共享资源有读和写的操作,且写操作没有读操作那么频繁。在没有写操作的时候,多个线程同时读一个资源没有任何问题,所以应该允许多个线程同时读取共享资源;但是如果一个线程想去写这些共享资源,就不应该允许其他线程对该资源进行读和写的操作了。大部分只是读数据,写数据很少,如果仅仅是读数据的话并不会影响数据正确性(出现脏读),而如果在这种业务场景下,依然使用独占锁的话,很显然这将是出现性能瓶颈的地方。针对这种读多写少的情况,java还提供了另外一个实现Lock接口的ReentrantReadWriteLock(读写锁)。读写锁允许同一时刻被多个读线程访问,但是在写线程访问时,所有的读线程和其他的写线程都会被阻塞。

接下来以缓存为例用代码演示读写锁,重现问题:

class MyCache{private volatile Map<String, String> cache= new HashMap<>();public void put(String key, String value){try {System.out.println(Thread.currentThread().getName() + " 开始写入!");Thread.sleep(300);cache.put(key, value);System.out.println(Thread.currentThread().getName() + " 写入成功!");} catch (InterruptedException e) {e.printStackTrace();} finally {}}public void get(String key){try {System.out.println(Thread.currentThread().getName() + " 开始读出!");Thread.sleep(300);String value = cache.get(key);System.out.println(Thread.currentThread().getName() + " 读出成功!" + value);} catch (InterruptedException e) {e.printStackTrace();} finally {}}
}public class ReentrantReadWriteLockDemo {public static void main(String[] args) {MyCache cache = new MyCache();for (int i = 1; i <= 5; i++) {String num = String.valueOf(i);// 开启5个写线程new Thread(()->{cache.put(num, num);}, num).start();}for (int i = 1; i <= 5; i++) {String num = String.valueOf(i);// 开启5个读线程new Thread(()->{cache.get(num);}, num).start();}}
}

打印结果:多执行几次,有很大概率不会出现问题

改造MyCache,加入读写锁

class MyCache{private volatile Map<String, String> cache= new HashMap<>();// 加入读写锁ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();public void put(String key, String value){// 加写锁rwl.writeLock().lock();try {System.out.println(Thread.currentThread().getName() + " 开始写入!");Thread.sleep(500);cache.put(key, value);System.out.println(Thread.currentThread().getName() + " 写入成功!");} catch (InterruptedException e) {e.printStackTrace();} finally {// 释放写锁rwl.writeLock().unlock();}}public void get(String key){// 加入读锁rwl.readLock().lock();try {System.out.println(Thread.currentThread().getName() + " 开始读出!");Thread.sleep(500);String value = cache.get(key);System.out.println(Thread.currentThread().getName() + " 读出成功!" + value);} catch (InterruptedException e) {e.printStackTrace();} finally {// 释放读锁rwl.readLock().unlock();}}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/39718.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

block()/blockFirst()/blockLast() 解决办法

定位到BlockingLoadBalancerClient.java 155行 出问题的点代码如下&#xff1a; Response<ServiceInstance> loadBalancerResponse Mono.from(loadBalancer.choose(request)).block();将这段修改为异步解决&#xff1a; 新建一个新的类 import org.springframework.c…

对Openai Chat API的一些理解

目录 偷懒的编写一个API 如何让ChatGPT理解我们都在聊什么 付费和一些注意事项 Create chat completion 最近ChatGPT这么火&#xff0c;那必须来凑个热闹啊。 申请账户我就不多说了&#xff0c;懂得都懂。 偷懒的编写一个API 从ChatGPT的Chat演示看&#xff0c;他需要一…

借AI之势,打破创意与想象的边界

IMMENSE、36氪&#xff5c;作者 01 “未来是属于AI的” 3月2日&#xff0c;内容创作圈大地震。 就在3月2日凌晨&#xff0c;OpenAI宣布开放ChatGPT本体模型API&#xff0c;其价格为1k tokens/$0.002。也就是说&#xff0c;从这一天开始&#xff0c;任何企业都能让ChatGPT为自…

谷歌的Bard怎么样?

Bard是什么&#xff1f; ChatGPT&#xff1a; Bard可以指以下几种事物&#xff1a; Bard是一个英雄联盟&#xff08;League of Legends&#xff09;游戏中的角色名称&#xff0c;他是一个能够进行攻击和治疗的辅助英雄。 Bard是指中世纪欧洲的一类文学艺术家&#xff0c;主要从…

实测「360智脑」的真正实力:能否领跑国内百“模”大战?

ChatGPT 的发布&#xff0c;无疑掀起了一股“AI 技术”新浪潮。百度文心一言、华为盘古、商汤日日新、阿里通义千问、讯飞星火等众多大模型的接连问世&#xff0c;使得国内的“百模之战”进入了前所未有的白热化阶段。无论是各大互联网巨头&#xff0c;还是清华、复旦等知名高校…

不止Chat,GPT-4 将释放更大生产力

目录 1.对 ChatGPT 的巨大超越 2.与 ChatGPT 相同的技术路线 3.GPT-4 背后的强大阵容 4.开启多模态大模型时代 相比 ChatGPT 能力有大进化&#xff0c;多模态上有突破但不多。 近日&#xff0c;多模态大模型 GPT-4 震撼登场&#xff01; GPT-4 能够接受图像和文本输入&am…

LoRA大模型加速微调和训练算法

ChatGPT带领着大模型像雨后春笋一般层出不穷&#xff0c;大家都对大模型微调跃跃欲试&#xff0c;现在咱们聊聊其中的常见的算法 1 LORA 低秩适应 理论 Lora( Low-Rank Adaotation)&#xff0c;低秩自适应模型微调的方法&#xff0c;它冻结预训练模型的权重&#xff0c;并将…

数据规模缩小 200 倍!指令微调高效指导大模型学习

夕小瑶科技说 原创 作者 | 智商掉了一地、Python 最近大型语言模型&#xff08;LLMs&#xff09;的指令微调备受研究人员的关注&#xff0c;因为它可以开发 LLM 遵循指令的潜力&#xff0c;使其更加符合特定的任务需求。虽然指令微调&#xff08;Instruction Tuning&#xff…

ChatGPT是否可以写出一篇论文

利用AI反哺教育和学术&#xff0c;在训练它写论文的过程中你学到的&#xff0c;比你自己写一篇论文学到的更多。让工具回归工具&#xff0c;让我们变成更好的我们&#xff01; 第一步&#xff1a;现象确认 第二步&#xff1a;学术概念化 第三步&#xff1a;定位优质的学术资源 …

网页版即时通讯聊天工具,支持主流浏览器,无需安装即可使用

基于信贸通即时通讯系统开发的网页版即时通讯&#xff0c;无需安装支持主流浏览器在线直接运行。可以与电脑版本和手机版本互通。支持文本聊天&#xff0c;标签&#xff0c;图片&#xff0c;文件传输&#xff0c;还支持位置接收等。 特点&#xff1a; 1、简单快速的集成到自己…

Ims跟2/3G会议电话(Conference call)流程差异介绍

2/3G Conference call 合并(Merged)通话前,两路电话只能一路保持(Hold),一路通话(Active)。 主叫Merged操作,Hold的一路会变成Active,进入会议通话。 例如终端A跟C通话,再跟B通话,此时B就是Active状态,C从Active变成Hold状态。Merged进入会议通话后,C又从Hold变…

英文学术会议参会必读-青年学者会议和演讲英语指南

本书介绍 本书讨论并展示在学术会议上使用的英语话语的类型&#xff0c;并从多角度为准会议参与者提供了指导。它是根据参加的众多学术会议的研究结果和作者的观察结果而得出的&#xff0c;基于对应用语言学的公认研究方法&#xff0c;以及针对学生&#xff0c;ESP老师&#xf…

jitsi-meet 主持人退出会议后结束会议室(网页访问)

实现功能&#xff1a; 当主持人退出当前会议后&#xff0c;要求参与会议的其他人员也都退出当前会议。 修改代码&#xff1a; 修改后&#xff0c;通过make编译代码。 将css/all.css 和 libs文件夹下的文件上传至会议服务器的对应目录下。重启会议服务器即可。 /usr/share/ji…

chatgpt赋能python:Python根据IP地址获取地理位置

Python根据IP地址获取地理位置 随着全球化的发展&#xff0c;网络已经成为人们获取信息和交流的主要渠道。在网站的开发和运营中&#xff0c;了解访问者的地理位置和所处时区是非常重要的。这样可以更好地定位目标受众并制定针对性的营销策略。本文将介绍如何使用Python根据IP…

一文读懂信息量、信息熵、相对熵(KL散度)和交叉熵

在人工智能深度学习的应用中&#xff0c;损失函数绝对是模型网络学习质量的关键。我们使用损失函数来表示的真实值与预测值之间的距离&#xff0c;进而指导模型的收敛方向。对于标量来说&#xff0c;我们能够很容易想到使用方差来描述误差。那么&#xff0c;如何表示向量之间的…

面板数据进行熵值法

面板数据熵值法分析流程如下&#xff1a; 一、案例背景 当前有9家公司连续5年&#xff08;2018-2022年&#xff09;的财务指标数据&#xff0c;想要通过这份数据&#xff0c;确定各个财务指标的权重。熵值法根据指标离散程度确定赋权大小&#xff0c;客观公正准确度高。本次收…

跨数据中心下的 Kafka 高可用架构分析

导语 本文介绍了 Kafka 跨数据中心的两种部署方式&#xff0c;简要分析两种方式下的不同架构以及优缺点&#xff0c;对这些架构可能碰到的问题也提供了一些解决思路&#xff1b;同时也说明了 Kafka 跨数据中心部署的社区解决方案和商业化解决方案。 背景 Kafka 作为世界上最…

分布式的流处理平台Kafka

目录&#xff1a; 一、简介二、基本概念三、生产者使用详解四、发送消息五、消费者代码示例 一、简介 ApacheKafka 是一个分布式的流处理平台。它具有以下特点&#xff1a; 支持消息的发布和订阅&#xff0c;类似于 RabbtMQ、ActiveMQ 等消息队列&#xff1b;支持数据实时处理…

熵_相对熵_散度

1 信息量 意外越大&#xff0c;越不可能发生&#xff0c;概率就越小&#xff0c;信息量也就越大&#xff0c;也就是信息越多。比如说“今天肯定会天黑”&#xff0c;实现概率100%&#xff0c;说了和没说差不多&#xff0c;信息量就是0。 详见&#xff1a;2. 信息量 1.1 公式 …

流批一体计算引擎-4-[Flink]消费kafka实时数据

Python3.6.9 Flink 1.15.2消费Kafaka Topic PyFlink基础应用之kafka 通过PyFlink作业处理Kafka数据 1 环境准备 1.1 启动kafka (1)启动zookeeper zkServer.sh start(2)启动kafka cd /usr/local/kafka/ nohup ./bin/kafka-server-start.sh ./config/server.properties >&g…