【Java 中锁的种类】

文章目录

  • 一、公平锁和非公平锁
  • 二、可重入锁(递归锁)
  • 三、自旋锁
  • 四、独占锁(写锁)/共享锁(读锁)/互斥锁


提示:以下是本篇文章正文内容,下面案例可供参考

一、公平锁和非公平锁

遵守先来后到的是公平锁,不遵守的是非公平锁

  1. synchronized 是非公平锁
  2. ReentrantLock 默认是非公平锁,可通过传入参数来变成公平锁。
	// 默认的无参构造方法是非公平锁的public ReentrantLock() {sync = new NonfairSync();}/*** fair == true,是公平锁* fair == false,是非公平锁*/public ReentrantLock(boolean fair) {sync = fair ? new FairSync() : new NonfairSync();}

优缺点

  • 公平锁能够保证任务的有序进行,所有的线程都能得到资源,不会饿死在队列中。缺点是吞吐量会下降很多,队列里面除了第一个线程,其他的线程都会阻塞,cpu唤醒阻塞线程的开销会很大。
  • 非公平锁可以减少CPU唤醒线程的开销,整体的吞吐量比公平锁高,CPU也不必取唤醒所有线程,会减少唤起线程的数量。缺点是可能导致队列中间的线程一直获取不到锁或者长时间获取不到锁,导致饥饿现象。

二、可重入锁(递归锁)

指的是同一线程外层函数获得锁之后﹐内层递归函数仍然能获取该锁的代码,在同一个线程在外层方法获取锁的时候,在进入内层方法会自动获取锁,也就是说线程可以进入任何一个它已经拥有的锁所同步着的代码块

synchronized 和 ReentrantLock 都是可重入锁

代码示例

class Phone implements Runnable{// 证明 synchronized 是可重入锁/*** 两个 synchronized 能够嵌套式使用* * */public synchronized void sendMessage() {// 调用该方法第一次加锁System.out.println(Thread.currentThread().getName() + "\tsendMessage");// 第二次加锁,使用 sendMessage 的锁,即共用同一把锁sendEmail();}public synchronized void sendEmail() {System.out.println(Thread.currentThread().getName() + "\tsendEmail");}// 证明 ReentrantLockLock lock = new ReentrantLock();@Overridepublic void run() {get();}public void get() {// ReentrantLock 需要手动加锁解锁// 但是要一一配对,只要配对,多少都可以// 如果这里 lock 两次,unlock 一次就会导致线程一直阻塞等待解锁lock.lock();
//        lock.lock();try {System.out.println(Thread.currentThread().getName() + "\tget");// 方法内部再次加锁set();} finally {// 不配对,会一直死锁lock.unlock();
//            lock.unlock();}}public void set() {lock.lock();try {System.out.println(Thread.currentThread().getName() + "\tset");} finally {lock.unlock();}}
}public class Main {public static void main(String[] args) {Phone phone = new Phone();new Thread(() -> {phone.sendMessage();}, "t1").start();new Thread(() -> {phone.sendEmail();}, "t2").start();Thread.sleep(1000);System.out.println();System.out.println("#######################");System.out.println();new Thread(phone, "t3").start();new Thread(phone, "t4").start();}
}

输出结果

嵌套调用成功,输出的顺序和我们预想的相同。

在这里插入图片描述

synchronized 加锁和释放锁的原理

monitorenter 和 monitorexit 指令,monitor计数器

monitorenter指令:

  1. monitor计数器为0,意味着目前还没有被获得,那这个线程就会执行一次 monitorenter 指令立刻获得这把锁,然后把锁计数器+1,一旦+1,别的线程再想获取,就需要等待。
  2. 如果这个monitor已经拿到了这个锁的所有权,又重入了这把锁,那锁计数器就会累加,变成2,并且随着重入的次数,会一直累加,但是累加的仅仅是锁计数器,不会再执行一次 monitorenter 指令。(即重复加锁,monitorenter 指令只执行一次,锁计数器不断累加
  3. 这把锁已经被别的线程获取了,等待锁释放。

monitorexit指令:将锁计数器减1,每执行一次 monitorexit 命令锁计数器就减1(这一点与 monitorenter 不同),当锁计数器为0,释放锁。


三、自旋锁

是指尝试获取锁的线程不会立即阻塞,而是采用循环的方式去尝试获取锁,这样的好处是减少线程上下文切换的消耗,缺点是循环会消耗CPU

synchronized 和 ReentrantLock 都是自旋锁,他们都会根据使用场景自动调整自旋时间

代码示例

class SpinLockDemo {// 原子引用线程AtomicReference<Thread> atomicReference = new AtomicReference<>();public void myLock() {// 此时 atomicReference 内的线程为 nullThread thread = Thread.currentThread();System.out.println(Thread.currentThread().getName() + "\tcome in");// CAS 自旋// 将 thread 内的线程赋给 atomicReference,一直自旋直到成功while (!atomicReference.compareAndSet(null, thread)) {}}public void myUnLock() {Thread thread = Thread.currentThread();// 解锁atomicReference.compareAndSet(thread, null);System.out.println(Thread.currentThread().getName() + "\tcome out");}
}public class Main {public static void main(String[] args) {SpinLockDemo spinLockDemo = new SpinLockDemo();new Thread(() -> {spinLockDemo.myLock();// 暂停一会线程,模拟一直占用,不能释放锁try {System.out.println("阻塞中......");Thread.sleep(5000);} catch (InterruptedException e) {e.printStackTrace();}spinLockDemo.myUnLock();}, "AA").start();}
}

输出结果

在这里插入图片描述


四、独占锁(写锁)/共享锁(读锁)/互斥锁

独占锁:指该锁一次只能被一个线程所持有。对 ReentrantLock 和 Synchronized 而言都是独占锁
共享锁:指该锁可被多个线程所持有

ReentrantReadWriteLock

  • 对 ReentrantReadWriteLock 其读锁是共亨锁,其写锁是独占锁。读写锁分离既保证了数据的一致性,又保证了高并发性
  • 读锁的共享锁可保证并发读是非常高效的,读写,写读,写写的过程是互斥的(互斥锁)
  • 写操作是独占的,原子的,不许分割。

代码示例

class MyCache {// 资源类(缓存操作,需要较高地并发操作)private volatile Map<String, Object> map = new HashMap<>();// 这个锁不足以满足,多个线程的同时读写
//    private Lock lock = new ReentrantLock();// 读写锁分离
//    private ReadWriteLock readWriteLock = new ReentrantReadWriteLock();// XXXXXXXXX/*** 写操作,不可共享* @param key* @param val* @throws InterruptedException*/public void put(String key, Object val) throws InterruptedException {try {// 拿到 writeLock,加锁
//            readWriteLock.writeLock().lock();// XXXXXXXXXSystem.out.println(Thread.currentThread().getName() + "\t正在写入:" + key);Thread.sleep(300);map.put(key, val);System.out.println(Thread.currentThread().getName() + "\t写入完成");} catch (Exception e) {e.printStackTrace();} finally {// 解锁
//            readWriteLock.writeLock().unlock();// XXXXXXXXX}}/*** 读操作,可以共享* @param key* @throws InterruptedException*/public void get(String key) throws InterruptedException {try {// 拿到 readLock 加锁
//            readWriteLock.readLock().lock();// XXXXXXXXXSystem.out.println(Thread.currentThread().getName() + "\t正在读取");Thread.sleep(300);System.out.println(Thread.currentThread().getName() + "\t读取完成:" + map.get(key));} catch (InterruptedException e) {e.printStackTrace();} finally {// 解锁
//            readWriteLock.readLock().unlock();// XXXXXXXXX}}
}public class ZZZZZZZZZZ {public static void main(String[] args) {MyCache myCache = new MyCache();// 写for (int i = 1; i <= 5; i++) {final int tmpInt = i;new Thread(() -> {try {myCache.put(tmpInt + "", tmpInt + "");} catch (InterruptedException e) {e.printStackTrace();}}, String.valueOf(i)).start();}// 读for (int i = 1; i <= 5; i++) {final int tmpInt = i;new Thread(() -> {try {myCache.get(tmpInt + "");} catch (InterruptedException e) {e.printStackTrace();}}, String.valueOf(i)).start();}}
}

输出结果(加锁前)

在这里插入图片描述

输出结果(加锁后)

将代码注释有XXXXXXXXX的代码解开,就是加锁后版本

在这里插入图片描述

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

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

相关文章

Mysql主从同步原理

文章目录 前言同步原理复制的核心流程写在最后 前言 随着社会的进步大家对服务端应用程序的性能指标有着越来越高的要求&#xff0c;比如响应时间、吞吐率、QPS、TPS等等。基本上大多数系统都会要求响应时间不超过3s&#xff0c;当然对吞吐量和并发量也会根据具体的业务场景进…

C#编程艺术:Fizzler库助您高效爬取www.twitter.com音频

数据是当今数字时代的核心资源&#xff0c;但是从互联网上抓取数据并不容易。本文将教您如何利用C#编程艺术和Fizzler库高效爬取Twitter上的音频数据&#xff0c;让您轻松获取所需信息。 Twitter简介 Twitter是全球最大的社交媒体平台之一&#xff0c;包含丰富的音频资源。用…

Android原生实现分段选择

六年前写的一个控件&#xff0c;一直没有时间总结&#xff0c;趁年底不怎么忙&#xff0c;整理一下之前写过的组件。供大家一起参考学习。废话不多说&#xff0c;先上图。 一、效果图 实现思路使用的是radioGroup加radiobutton组合方式。原理就是通过修改RadioButton 的backgr…

初始JVM

目录 一、什么是JVM 二、JVM与字节码 三、Java程序运行机制 四、JVM 的主要组成部分及其作用 一、什么是JVM JVM 本质上是一个运行在计算机上的程序&#xff0c;他的职责是运行Java字节码文件 二、JVM与字节码 三、Java程序运行机制 首先利用IDE集成开发工具编写Java源代码…

Docker 部署RAP2

1、Github介绍 https://github.com/thx/rap2-delos 2、安装Docker环境 yum install -y yum-utils device-mapper-persistent-data lvm2 yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo yum install -y docker-ce systemctl enable…

环形链表、环形链表 II、有效的括号​​​​​​​(leetcode)

目录 一、环形链表 方法&#xff08;快慢指针&#xff09;&#xff1a; 二、环形链表 II 三、有效的括号 一、环形链表 给你一个链表的头节点 head &#xff0c;判断链表中是否有环。 如果链表中有某个节点&#xff0c;可以通过连续跟踪 next 指针再次到达&#xff0c;则链…

C# 图标标注小工具-查看重复文件

目录 效果 项目 代码 下载 效果 项目 代码 using System; using System.Collections.Generic; using System.Data; using System.IO; using System.Linq; using System.Security.Cryptography; using System.Windows.Forms;namespace ImageDuplicate {public partial clas…

SparkSQL 执行底层原理解析

从Spark SQL 底层架构可以看到&#xff0c;我们写的SQL语句&#xff0c;经过一个优化器&#xff08;Catalyst&#xff09;处理&#xff0c;转化为可执行的RDD&#xff0c;提交给集群执行。 SQL到RDD中间经过了一个Catalyst&#xff0c;它便是Spark SQL的核心&#xff0c;是针对…

基于医疗AI、自然语言处理技术的智能导诊系统源码,java语言开发,自主版权,可扩展至H5、小程序、app等多端

智能导诊系统源码&#xff0c;自主研发&#xff0c;演示应用案例 一、系统概述&#xff1a; 人体智能导诊系统&#xff1a;是基于医疗AI、自然语言处理技术&#xff0c;推出的在线导医分诊智能工具&#xff0c;在医疗中使用的引导患者自助就诊挂号。 在就诊的过程中有许多患者…

QString的处理及中文乱码问题

QString 是 Qt 框架中用于表示字符串的一个类。它提供了丰富的功能来处理 Unicode 字符串&#xff0c;使得国际化和本地化的应用程序开发更加简单。QString 与标准 C 的 std::string 类似&#xff0c;但提供了更多与 Unicode 和国际化相关的功能。 常用功能 判空 代码演示 is…

计算机网络复习1

概论 文章目录 概论计算机网络的组成功能分类性能指标&#xff08;搞清楚每个时延的具体定义&#xff09;分层结构协议、接口和服务服务的分类ISO/OSITCP/IP两者的不同 计算机网络的组成 组成部分&#xff1a;硬件&#xff0c;软件和协议&#xff08;协议&#xff1a;传输数据…

HPCC:高精度拥塞控制

HPCC&#xff1a;高精度拥塞控制 文章目录 HPCC&#xff1a;高精度拥塞控制摘要1 引言1.1 背景1.2 现有CC的局限性1.3 HPCC的提出 2 研究动机2.1 大型RDMA部署2.2 RDMA目标2.3 当前RDMA CC中的权衡DCQCNTIMELY 2.4 下一代高速CC 3 技术方案3.1 INT3.2 HPCC设计3.3 HPPC的参数 4…

【力扣题解】P404-左叶子之和-Java题解

&#x1f468;‍&#x1f4bb;博客主页&#xff1a;花无缺 欢迎 点赞&#x1f44d; 收藏⭐ 留言&#x1f4dd; 加关注✅! 本文由 花无缺 原创 收录于专栏 【力扣题解】 文章目录 【力扣题解】P404-左叶子之和-Java题解&#x1f30f;题目描述&#x1f4a1;题解&#x1f30f;总结…

计算机毕业设计-----ssm流浪猫狗救助管理系统

项目介绍 流浪猫狗救助管理系统。该项目分为前后台&#xff1b; 前台主要功能包括&#xff1a;会员的注册登陆,流浪猫狗知识&#xff0c;领养中心&#xff0c;团队活动&#xff0c;流浪宠物详情&#xff0c;申请领养等&#xff1b; 后台主要功能包括&#xff1a;管理员的用户…

MySQL 核心模块揭秘 |《发刊词》

1. 为什么要写专栏&#xff1f; 我还在做业务系统研发的时候&#xff0c;有一段时间&#xff0c;系统不稳定&#xff0c;慢 SQL 很多。我们团队花了很长时间持续优化 SQL。 我们有一个表格&#xff0c;从慢查询日志里整理出了很多慢 SQL。其中一些 SQL&#xff0c;按照我们的…

详细讲解Java使用EasyExcel函数来操作Excel表(附实战)

目录 前言1. EasyExcel类2. 原理分析3. demo4. 实战 前言 前阵时间好奇下载Excel&#xff0c;特意学习实战了该功能&#xff1a;详细讲解Java使用HSSFWorkbook函数导出Excel表&#xff08;附实战&#xff09; 现在发觉还有个EasyExcel也可专门用来读写Excel表 1. EasyExcel类…

uni-app uni.scss内置全局样式变量

锋哥原创的uni-app视频教程&#xff1a; 2023版uniapp从入门到上天视频教程(Java后端无废话版)&#xff0c;火爆更新中..._哔哩哔哩_bilibili2023版uniapp从入门到上天视频教程(Java后端无废话版)&#xff0c;火爆更新中...共计23条视频&#xff0c;包括&#xff1a;第1讲 uni…

python3 函数

Python 定义函数使用 def 关键字&#xff0c;一般格式如下&#xff1a; def 函数名&#xff08;参数列表&#xff09;&#xff1a;函数体 让我们使用函数来输出"Hello World&#xff01;"&#xff1a; >>> def hello() :print("Hello World!") &…

深入浅出图解C#堆与栈 C# Heap(ing) VS Stack(ing) 第三节 栈与堆,值类型与引用类型

深入浅出图解C#堆与栈 C# Heaping VS Stacking 第三节 栈与堆&#xff0c;值类型与引用类型 [深入浅出图解C#堆与栈 C# Heap(ing) VS Stack(ing) 第一节 理解堆与栈](https://mp.csdn.net/mdeditor/101021023)[深入浅出图解C#堆与栈 C# Heap(ing) VS Stack(ing) 第二节 栈基本工…

【项目】玩具租赁博客测试报告

目录 一、项目背景 二、项目功能 三、功能测试 一、项目背景 玩具租赁系统采用前后端分离的方法来实现&#xff0c;同时使用了数据库来存储相关的数据&#xff0c;同时将其部署到云服务器上。前端主要有十五个页面构成&#xff1a;用户注册、管理员注册、登录页、用户和管理…