JAVAEE——多线程进阶,锁策略

文章目录

  • 锁策略
    • 乐观锁和悲观锁
      • 乐观锁
      • 悲观锁
        • 两者的比较
    • 读写锁
    • 重量级锁和轻量级锁
      • 重量级锁
      • 轻量级锁
    • 自旋锁
    • 公平锁和非公平锁
      • 公平锁
      • 非公平锁
    • 可重入锁和不可重入锁
      • 可重入锁
      • 不可重入锁

锁策略

乐观锁和悲观锁

乐观锁

什么是乐观锁呢?我们可以认为乐观锁比较自信,当多线程执行的时候如果要对一个资源进行访问,那么乐观锁会认为这些线程的访问是不会造成冲突的,并且这些访问也不都是要进行修改。因此对这些线程访问更新的时候乐观锁才会进行检测查看这个进程的访问是否会造成冲突,如果会的话就返回错误信息由用户决定是否如何去做

悲观锁

悲观锁相较于乐观锁也是一种极端,悲观锁认为只要访问就一定会造成并发冲突,因此悲观锁认为只要是访问资源,都必须加上锁,这样当别人想要获取这个资源的时候就必须获取这把锁陷入了阻塞等待当中。

两者的比较

这里我们举一个例子来说明

假如说甲乙两位同学向老师请问问题,那么甲是一个悲观的人,乙是一个乐观的人,甲突然去找老师,老师肯定在忙碌无法给自己将题目,因此会先给老师发个消息询问老师是否在忙碌当得到老师的答复之后再决定是继续等待还是直接过去,而乙则是比较乐观,认为自己现在过去不会打扰到老师,老师可以立即解答自己的疑惑,因此乙就会直接过去询问老师问题,这时候可能老师确实没有忙碌可以直接解决问题也有可能老师是在忙碌的,这时候乙就需要进行等待了。


结论:两把锁各有特点针对于不同的场合使用不同的锁没有什么优劣性比较。

读写锁

读写锁。线程对一个资源的访问是分为读操作和写操作的,那么对于一个资源来说读操作是不会导致线程不安全的只有写操作才会导致线程不安全,因此线程对一个资源的访问加锁我们也可以分为读锁和写锁来进行,因此我们就需要搞明白哪些情况会导致线程不安全

  • 线程同时去读取一个数据:安全的
  • 一批线程去读另一批线程在写:不安全
  • 线程都在写:不安全

因此读写锁,也就诞生了,那么读写锁是怎么进行加锁的呢?按照字面意思肯定就是按照一个线程是读还是写来区别加锁了。那么既然是按照意图来进行区别加锁的我们首先当然要清除这个线程想要进行的操作是什么才可以来进行加锁。也就是加锁前需要获取这个线程的行为意图。
读写锁就是把读操作和写操作区分对待. Java 标准库提供了 ReentrantReadWriteLock 类, 实现了读写
锁.
我们来写一个示例代码运行一下查看

import java.util.concurrent.locks.ReentrantLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

public class Lock_policy  {
    public static  int tmp=10;
    public static final ReentrantReadWriteLock wrlock=new ReentrantReadWriteLock();
    public static void main(String[] args) {
            Thread t1=new Thread(()->{
                wrlock.readLock().lock();
                System.out.println("我是t1线程"+tmp);
                //wrlock.readLock().unlock();

            });
            Thread t2=new Thread(()->{
                wrlock.readLock().lock();
                System.out.println("我是t2线程"+tmp);
                //wrlock.readLock().unlock();
            });
            Thread t3=new Thread(()->{
                wrlock.writeLock().lock();
                tmp++;
                System.out.println("我是t3线程"+tmp);
            });
            t1.start();
            t2.start();
    }
}

首先我们可以观察到这个代码中我们创建的t3线程并没有start运行因此我们测试的是当两个线程同时加上读锁并且读一个数据的时候是否会阻塞我们看一下运行结果。
在这里插入图片描述
结果很明显程序正常跑完因此没有任何的阻塞。我们来试试把t3线程运行试试结果。

import java.util.concurrent.locks.ReentrantLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

public class Lock_policy  {
    public static  int tmp=10;
    public static final ReentrantReadWriteLock wrlock=new ReentrantReadWriteLock();
    public static void main(String[] args) {
            Thread t1=new Thread(()->{
                wrlock.readLock().lock();
                System.out.println("我是t1线程"+tmp);
                //wrlock.readLock().unlock();

            });
            Thread t2=new Thread(()->{
                wrlock.readLock().lock();
                System.out.println("我是t2线程"+tmp);
                //wrlock.readLock().unlock();
            });
            Thread t3=new Thread(()->{
                wrlock.writeLock().lock();
                tmp++;
                System.out.println("我是t3线程"+tmp);
            });
            t1.start();
            t2.start();
            t3.start();
    }
}

运行截图如下
在这里插入图片描述
那么是否只有这一种结果呢?当然不是。
在这里插入图片描述
在实验的过程中我们可能产生这些不同的结果那么这是为什么呢?原因很简单那就是因为t3线程在t1和t2线程获取读锁之前就已经加上了写锁导致了t1和t2的读操作阻塞了。那么无论上述的哪种 结果都是可以说明读写锁中读锁和写锁是阻塞的。

重量级锁和轻量级锁

重量级锁

在理解这里的时候我们要先搞明白什么是重量级锁。顾名思义听名字就感觉这个锁很慢,那么这种原因究竟是为什么呢?这就要追溯到锁的一个机制了。


我们要搞明白加锁是为了干什么?很简单加锁是为了保证我们的多个操作是原子性的。原子性的保证使得我们的操作避免了多线程的安全问题。那么为什么锁可以保证操作的原子性呢?其实说白了还是因为我们的硬件设备的支持也就是底层建筑决定上层建筑,因为我们的硬件支持,所以我们的操作系统可以给我们提供一系列的锁的操作接口,而操作系统提供的接口就是mutex。

  • CPU 提供了 “原子操作指令”.
  • 操作系统基于 CPU 的原子指令, 实现了 mutex 互斥锁.
  • JVM 基于操作系统提供的互斥锁, 实现了 synchronized 和 ReentrantLock 等关键字和类
    在这里插入图片描述

重量级锁:加锁机制非常依赖于OS提供的mutex因此在加锁的时候涉及到大量的从内核态到用户态的转变,效率较低。

轻量级锁

轻量级锁即加锁的时候尽量不依赖于mutex而是尽量在用户态代码完成. 实在搞不定了, 再使用 mutex。那么这时候有些美女或者帅哥可能就会很混乱,说的直白一些就是,轻量级锁就是尽量不去调用系统接口他就是一个优化。

自旋锁

什么是自旋锁呢?那么按照我们之前写的代码当我们的锁被抢占的时候如果我们也想抢占这把锁的话就需要先进行阻塞等待也就是wait然后等待被唤醒,可是我们不希望这样怎么办?也就是说可能当前占有锁的这个进程很快就结束了就要释放这把锁了

公平锁和非公平锁

公平锁

公平锁遵守先来后到的原则B比C先来的那么当A释放锁之后B就会比C先获取到释放的锁

非公平锁

不遵守先来后到的原则,synchronized()就是一个非公平锁,他的调度顺序是有操作系统决定的。

  • 操作系统内部的线程调度就可以视为是随机的. 如果不做任何额外的限制, 锁就是非公平锁. 如果要
    想实现公平锁, 就需要依赖额外的数据结构, 来记录线程们的先后顺序.
  • 公平锁和非公平锁没有好坏之分, 关键还是看适用场景.

可重入锁和不可重入锁

可重入锁

可重入的意思就是同一个线程可以多次获取同一把锁。也就是可以重新进入的锁。synchorinzed就是一把可重入锁。Java里只要以Reentrant开头命名的锁都是可重入锁,而且JDK提供的所有现成的Lock实现类,包括
synchronized关键字锁都是可重入的。

不可重入锁

不可重入锁就是当自己这个线程要重新申请这把锁的时候要进行等待便是不可重入锁。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

树的重心——树的结构

树的重心是指对于某个点,将其删除后,可以使得剩余联通块的最大值最小。也就等价于一某个点为根的树,将根删除后,剩余的若干棵子树的大小最小。 例如下图的树的重心就是2。 性质: 性质一:重心的若干棵子树打…

代码第三十六天:需要添加的硬币的最小数量

需要添加的硬币的最小数量 题目要求: 解题思路 为方便描述,把 0 也算作可以得到的数。 假设现在得到了区间 [ 0 , s − 1 ] [0,s−1] [0,s−1] 中的所有整数,如果此时遍历到整数 x c o i n s [ i ] xcoins[i] xcoins[i],那么…

百度云加速方法「Cheat Engine」

加速网盘下载 相信经常玩游戏的小伙伴都知道「Cheat Engine」这款游戏内存修改器,它除了能对游戏进行内存扫描、调试、反汇编 之外,还能像变速齿轮那样进行本地加速。 这款专注游戏的修改器,被大神发现竟然还能加速百度网盘资源下载&#xf…

Linux 学习之路 -- 进程篇 -- 背景介绍

目录 1、冯诺依曼体系架构 2.操作系统 1、冯诺依曼体系架构 再开始学习进程之前我们要先了解一下计算机的体系结构,这里我们以最经典的冯诺依曼体系结构为例,简单介绍一下一下计算机的体系结构,方便我们对进程的理解。 这里的中央处理器就是…

【C++】入门C++(上)

简单唠几句 从今天开始我们就要进入C的学习了 众所周知,C是在C语言的基础上应运而生的,其容纳进去了面向对象编程思想,并增加了许多有用的库,以及编程范式,为我们在编程上提供了很大的便捷 在接下来的这几篇C入门的博…

设计方案-定时任务接口数据存储及更新策略

前言 在没有使用ETL工具且不考虑多数据源的情况下,我们需要从别的系统获取数据时,一般会选择分页接口查询并存储。本文算是我对类似场景代码的提炼,旨在总结相关套路,提升自我对数据库和模块的设计能力。 ETL(英文 Extract-Trans…

微分方程数值解法_常微分方程篇

一阶常微分方程初值问题 问题的适定性 (well-posedness): (數學系的角度) • 存在性:问题有解 • 唯一性:解是唯一的 • 稳定性:这个唯一解连续地依赖于问题中所给的数据(即初值、边值等) 初值问题的求解 Euler 法 區別(極限) 入門 要點:極限、中值定理==>差分方程…

linux进程退出之exit与_exit

linux进程退出之exit与_exit _exitexit流程清理函数atexit()函数:on_exit()函数: _exit /* Terminate program execution with the low-order 8 bits of STATUS. */ /** status参数定义了进程的终止状态,父进程可以通过wait(&am…

leetcode刷题---链表

目录 1.删除链表的倒数第N个节点两两交换链表中的节点反转链表2 1.删除链表的倒数第N个节点 根据题目描述,第一个思路是存到数组中对数组进行操作,想到数组我们就可以想到下标和倒数第N个的关系,所以我们可以不额外开空间,可以直接…

vuex插件实现数据共享

vuex插件 vuex是管理多个vue通用的数据的插件.(状态管理工具,状态是数据) 我们对于多个vue文件之间的共同数据,是用props传递,或者对于一个vue实例对象,进行绑定,传参,也是多次传参,多个文件之间,比较麻烦. 但是我们vuex会创建一个公共对象,从这个公共对象上赋值,比较简单易…

appium辅助自动化工具-- Appium studio

这里我要给大家介绍一款appium辅助自动化测试工具appium studio,你没看错,不是android studio,也不是appium android studio,就是appium studio! 下载地址: Appium Studio | Digital.ai Continuous Test…

【应用笔记】LAT1413+快速开关蓝牙导致设备无广播

1. 问题背景 客户使用 BlueNRG-345MC 开发了一个 BLE 外设,和手机连接。在测试中发现,手机连接上外设之后,不断地在手机上点击蓝牙的开关按钮,造成设备不断地断开、重连;少则几次,多则几十次。点击之后&am…

【小贪】万字长文介绍因果推断和增益模型

文章目录 因果推断和增益模型1. 绪论2. 因果推断基础3. 主要增益模型3.1 Meta Learning3.1.1 S-Learner(One Model)3.1.2 T-Learner(Two Model)3.1.3 R-Learner3.1.4 X-Learner3.1.5 类别转换法(Class Transformation …

2024年noc指导教师认证测评参考试题题目5-6合集

[noc指导教师认证] 测评参考试题 说明:NOC教师指导认证考试题目是从题库里抽题,因此每位老师每次考试题目都不一样以下题目为测试考试时收集到的一些题目,作为辅助提供给各位老师,老师们可以记住题目及答案的具体内容 (选项顺序会变),以免考试时遇到。2024年的做的题目有的…

.Websalm勒索病毒数据恢复|金蝶、用友、管家婆、OA、速达、ERP等软件数据库恢复

导言: 在数字化时代,网络安全问题日益凸显,其中勒索病毒作为一种新型的电脑病毒,以其独特的传播方式和恶劣的性质,给广大用户带来了巨大的困扰。近期,Websalm勒索病毒成为了公众关注的焦点,其强…

【图轮】【 最小生成树】【 并集查找】1489. 找到最小生成树里的关键边和伪关键边

本文涉及知识点 图轮 最小生成树 并集查找 关键边 1489. 找到最小生成树里的关键边和伪关键边 给你一个 n 个点的带权无向连通图,节点编号为 0 到 n-1 ,同时还有一个数组 edges ,其中 edges[i] [fromi, toi, weighti] 表示在 fromi 和 to…

【C++庖丁解牛】自平衡二叉搜索树--AVL树

🍁你好,我是 RO-BERRY 📗 致力于C、C、数据结构、TCP/IP、数据库等等一系列知识 🎄感谢你的陪伴与支持 ,故事既有了开头,就要画上一个完美的句号,让我们一起加油 目录 前言1 AVL树的概念2. AVL…

【A-006】基于SSH的新闻发布系统(含论文)

【A-006】基于SSH的新闻发布系统(含论文) 开发环境: Jdk7(8)Tomcat7(8)MySQLIntelliJ IDEA(Eclipse) 数据库: MySQL 技术: SpringStruts2HiberanteJSPJquery 适用于: 课程设计,毕业设计&…

玩转Django分页器

一、Pagination 分页器编程步骤 View, 导入django.core.paginator.Paginator类,创建Paginator 对象时,输入qs对象,以及每页显示条数。 接收 URL, 从请求参数中读取page数值 ,通过 paginator.page(page_num) 返回请求页的page_obj…

ObjectiveC-05-复杂和特殊数据类型

这一节中会详细介绍下ObjectiveC中的复杂数据类型,这些类型不太是太归类。但非常有用,有的用于定义变量、有的则是专门用于方法的返回值。 常用的大概有如下这些: 以上这些特殊的数据类型都可用于变量、方法返回值、方法参数使用&#xff0c…