3.10多线程
一.常见锁策略
1.悲观锁 vs乐观锁
体现在处理锁冲突的态度
①悲观锁:预期锁冲突的概率高
所以做的工作更多,付出的成本更多,更低效
②乐观锁:预期锁冲突的概率低
所以做的工作少,付出的成本更低,更搞笑
2.读写锁 vs 普通的互斥锁
①普通的互斥锁,只有两个操作 加锁和解锁
只有两个线程针对同一个对象加锁,就会产生互斥
②读写锁:有三个操作

针对读锁和读锁之间,是不存在互斥的关系--->因为多线程同时读一个变量,不会有线程安全的问题
读锁和写锁之间,写锁和写锁才需要互斥
而且很多场景中,都是读多写少
3.重量级锁 vs 轻量级锁
是为了处理锁冲突的结果
重量级锁 做了更多的事情,开销大,轻量级锁反之亦然
大多数情况下,悲观锁一般都是重量级锁,乐观锁一般都是轻量级锁
比如: 在使用的锁中,如果锁是基于内核的一些功能来实现的(比如调用了操作系统提供的mutex接口).一般认为这是重量级锁
如果锁是纯纯用户态实现的,一般认为这是轻量级锁,因为用户态的代码更可靠,也更高效
4.挂起等待锁 vs 自旋锁
①挂起等待锁:通过内核的一些机制来实现的.往往比较中 [重量级锁的一种典型实现]
②自旋锁 : 往往是通过用户态代码来实现的,往往比较轻,[轻量级锁的一种典型实现]\
5.公平锁 vs 非公平锁
公平锁:多个线程在等待一把锁的时候谁是先来的,谁就能先获取到这个锁(遵守先来后到
非公平锁:多个线程在等待一把锁的时候不遵守先来后到,每个等待的线程获取到锁的概率是均等的
对于操作系统来说->线程之间的调度是随机的(机会是均等的) .操作系统提供的mutex这个锁.就是属于非公平锁
6.可重入锁 vs 不可重入锁
可以嵌套加锁,就是可重入锁,会死锁的就是不可重入锁
7.synchronized(面试
1)即是一个乐观锁.也是一个悲观锁(根据锁竞争的激烈程度,自适应)
2)不是读写锁,只是一个普通互斥锁
3)既是一个轻量级锁,也是一个重量级锁,(根据锁竞争的激烈程度,自适应)
4)非公平锁 5)可重入锁
二.CAS
compare(比较) and swap(交换)
1.定义
CAS锁:拿着寄存器或者/某个内存的值和另外一个内存的值进行比较,如果值相同了,就交换

伪代码

CAS:提供一个单纯的CAS指令,通过这一条指令,就完成上述伪代码描述的过程
CAS指令:如果上述伪代码的过程只用一条做完,就是原子性,这个时候就是线程安全了
2.作用
1)基于CAS能够实现原子类
(java标准库里提供了一组原子性,针对常用的int.long,,int[]进行了封装,基于cas方式修改,并且线程安全


这段代码是不存线程安全的问题
基于CAS实现的++操作
因为这里既能保证线程安全,又能比synchronized高效
synchronized会涉及到锁的竞争,又要涉及线程的阻塞等待

这里的oldvalue变量,实际实际上可能使用一个寄存器来存的

这个赋值操作就相当于把数据从内存读到寄存器中(load

判定一下当前内存的值是不是和刚刚寄存器取到的值一直
如果判定成功,那就把value设为oldvalue+1.并返回true 循环结束
如果判定失败,继续下次循环,返回true;下次循环还是要先读一下value

这两行代码之间,很有可能有其他线程修改了value的值

为什么上述++操作线程安全
load
cas


2).基于CAS实现自旋锁

自旋锁是一个轻量级锁,也可以视为一个乐观锁
这把锁虽然没能够立即拿到,预期很快就能拿到(假设锁冲突不激烈)
3.ABA问题
1)定义
CAS中的关键就是先比较再交换
比较其实在比较当前值和旧值是否相同
把这两个值相同,就视为中间没有发生改变
但是这句话有漏洞,可能当前值和旧值相同的情况是因为变了然后又变回来了, 这就是ABA
2.案例
举例:ABA产生的BUG
假设小红账户余额 100 他想要取50
当按下取钱操作的时候,机器卡了一下,小红一紧张多按了一下取款
这就相当于 一次取钱操作 执行了两遍 (两个线程,并发的去执行这个取钱操作)
预期是只能取成功一次
如果基于CAS来实现的话

本来应该没问题
第二个线程再比较的时候就会发现,读到的100和原本的50不一样,就会不操作了
但是在取款的一瞬间,小红额度朋友给他转账了50.这个时候就会触发的ABA问题

这个时候第二个线程就会发现还是100.于是又扣了50

3)解决方法
引入版本号
这个版本号只能变大,修改变量的时候,比较的不是变量而是比较版本号


当引入版本号以后,t2再次尝试进行这里的比较版本操作
就会发现版本的旧值和当前的值并不匹配,就直接放弃修改
或者加入时间戳
看当前读到的内存改变的时间戳有没有变化

三.synchronized锁优化机制
只考虑1.8情况
1.锁膨胀/锁升级
体现了synchronized能够"自适应"这样的能力

偏向锁:并不是真的加锁,只是做了个标记.带来的好处就是后续没有竞争额时候,就避免了加锁解锁的开销
比如在大学校园里,大家都很讲素质,所以我的自行车都不锁->我只是在上面刻了我的名字做个标记
但是有一天好几个人的自行车都丢了,.开始有竞争的现象.我就开始加锁.进入轻量级锁
2.锁粗化/锁细化
此处的粗细指的是"锁的粒度"---->加锁代码涉及到的范围,范围越大,就认为锁的粒度越粗.反之亦然

锁粒度细 并发高
锁粒度粗.加锁解锁的开销就小
但是编译器会优化,如果某个地方代码所太细了,就会进行自动粗化
如果加锁之间间隔少(中间的代码少) 就很可能触发这个优化
比如:我给老师打电话,让他帮我请假.然后又打电话说我要出校,然后紧接着又打电话说我想借钱
每次给老师打电话都是给老师加锁,老师都不能接其他人的电话,加锁解锁开销大 粒度太细
于是可以粗化,我一次性说完.再挂电话
3.锁消除
有时候不小心加锁了
编译器发现这个锁没必要就会自动消除
比如StringBuffer Vector 在标准库里都进行了加锁操作
如果在单线程用到了上述的类,编译器会自动解锁.
四.java中的JUC
JUC ------java.util.concurrent
concurrent 并发的
1.Callable
是一个interface 也是一种创建线程的方式
因为Runnable 不太适合让线程计算一个结果

而如果用Callable就比较容易
public static void main1(String[] args) throws InterruptedException {Result result=new Result();Thread t=new Thread(){@Overridepublic void run() {int sum=0;for (int i = 0; i < 100; i++) {sum+=i;}synchronized (result.locker){result.sum=sum;//如果不加锁额度话,这是一个写操作,可能会导致线程不安全的情况,需要加锁result.locker.notify();}}};t.start();synchronized (result.locker){while(result.sum==0){result.locker.wait();}}//加锁,直到赋值了才可以唤醒才可以往下执行System.out.println(result.sum);//这里还需要创建一个实例让他们的sum互相传}

public static void main2(String[] args) {//构建一个类来描述任务Callable<Integer> callable=new Callable<Integer>() {@Overridepublic Integer call() throws Exception {int sum=0;for (int i = 0; i < 100; i++) {sum+=i;}return sum;}};//因为线程的构造方法不能识别callable 需要创建一个类来识别,FutureTask<Integer> task=new FutureTask<>(callable);Thread thread=new Thread(task);thread.start();//凭借task能执行//如果线程里的任务没执行完,task.get(就会一直阻塞,直到任务完成try {System.out.println(task.get());} catch (InterruptedException e) {e.printStackTrace();//这是任务没执行完阻塞} catch (ExecutionException e) {e.printStackTrace();//这是任务出错执行}}
2.ReentrantLock
可重入锁,注意synchronized也是可重入锁
1)基础用法
lock()
unlock()
把加锁和解锁两个操作给分开了
这中分开额度做法不太好,容易出现死锁
当出现多个线程竞争同一把锁的时候就会阻塞

2)与synchronized区别(面试)
1.synchronized是一个关键字(背后的逻辑是由JVM实现的也就是c++代码)
ReentrantLock是标准库里的一个类 背后的逻辑是java代码写的
2,synchronized不需要手动释放锁,出代码块,锁自动释放,
ReentrantLock必须要手动释放锁,要谨防忘记释放
3.synchronized是一个非公平锁
ReentrantLock提供了非公平锁和非公平锁 在构造方法中,指定参数来 默认是false

4.synchronized如果竞争锁额度时候失败,就会阻塞等待
ReentrantLock不仅可以阻塞等待,还可以trylock 失败了直接返回
5.synchronized衍生出来的等待机制,是wait notify 功能是相对有限的
ReentrantLock衍生出来的等待机制,是Condition.功能比较丰富
3.Semaphore
信号量 是一个更广义的锁
是锁里的一种特殊情况,叫做二元信号量

这个牌子就是信号量,描述了可用资源的个数
每次申请一个可用资源,计数器就-1(也就是P操作 ->acquire 申请
每次释放一个可用资源 计数器就+1 也就是V操作 ->release 释放
当信号量的计数为0 再次进行p操作,就会阻塞等待
锁就可以视为二元信号量.可用资源就一个,计数器的取值非0即1

4.CountDownLatch
终点线
countdown 给每个线程里面调用就表示到达终点
await 给等待线程去调用 所有任务都到达终点了.awit就阻塞返回 就表示任务完成

当所有的选手都冲过终点才认为是比赛结束
多线程下载


public static void main(String[] args) throws InterruptedException {CountDownLatch countDownLatch=new CountDownLatch(10);//表示有10个选手for (int i = 0; i < 10; i++) {Thread t =new Thread(()->{try {Thread.sleep(3000);System.out.println(Thread.currentThread().getName()+"已到达");countDownLatch.countDown();//记录次数} catch (InterruptedException e) {e.printStackTrace();}});t.start();}//当十个线程都执行完,await才停止阻塞countDownLatch.await();System.out.println("比赛结束");}
五.哈希表
HashMap本身是线程不安全的
1.HashTable->推荐
2.ConcurrentHashMap->不推荐

针对this加锁,只要有多个线程来访问HashTable,就会加锁

假设员工想请假,就得当面来申请,他好签字
由于公司里的人很多,很多人都要请假,这个时候大家都在老板门口排队,非常不方便
每个HashTable都只有一把锁

解决方法
ConcurrentHashMap

每个链表头加锁
1.面试问题-CconcurrentHashMap和HashTable的关系
1.ConcurrentHashTable只是减少了锁冲突,就让锁加到每个链表的头节点
2,ConcurrentHashMap只是针对写操作 读操作没加锁
3,ConcurrentHashMap广泛使用CAS,
4,ConcurrentHashMap针对扩容,巧妙的化整为零

相关文章:

3.10多线程
一.常见锁策略1.悲观锁 vs乐观锁体现在处理锁冲突的态度①悲观锁:预期锁冲突的概率高所以做的工作更多,付出的成本更多,更低效②乐观锁:预期锁冲突的概率低所以做的工作少,付出的成本更低,更搞笑2.读写锁 vs 普通的互斥锁①普通的互斥锁,只有两个操作 加锁和解锁只有两个线程针…...

缓存双写一致性之更新策略探讨
问题由来 数据redis和MySQL都要有一份,如何保证两边的一致性。 如果redis中有数据:需要和数据库中的值相同如果redis中没有数据:数据库中的值是最新值,且准备会写redis 缓存操作分类 自读缓存读写缓存: ࿰…...
scala高级函数快速掌握
scala高级函数一.函数至简原则二.匿名的简化原则三.高阶函数四.柯里化和闭包五.递归六.抽象控制七.惰性加载🔥函数对于scala(函数式编程语言)来说非常重要,大家一定要学明白,加油!!!…...

手写模拟SpringMvc源码
MVC框架MVC是一种设计模式(设计模式就是日常开发中编写代码的一种好的方法和经验的总结)。模型(model)-视图(view)-控制器(controller),三层架构的设计模式。用于实现前端…...

五分钟了解JumpServer V2.* 与 v3 的区别
一、升级注意项 1、梳理数据。JumpServer V3 去除了系统用户功能,将资产与资产直接绑定。当一个资产名下有多个同名账号,例如两个root用户时,升级后会自动合并最后一个root,不会同步其他root用户。升级前需保证每一个资产只拥有一…...

用友开发者中心应用构建实践指引!
基于 iuap 技术底座,用友开发者中心致力于为企业和开发者提供一站式技术服务,让人人都能轻松构建企业级应用。 本文以人力资源领域常用的应聘人员信息登记与分析功能为例,详细介绍如何在用友开发者中心使用 YonBuilder 进行应用构建。 功能…...

snap使用interface:content的基础例子
snap做包还在学习阶段,官网文档可查看:The content interface | Snapcraft documentation该例子由publiser和consumer两部分组成,一个提供一个只读的数据区,一个来进行读取其中的信息,这样就完成了content的交互。publ…...
蓝桥杯刷题第七天
第一题:三角回文数问题描述对于正整数 n, 如果存在正整数 k 使得2n123⋯k2k(k1), 则 n 称为三角数。例如, 66066 是一个三角数, 因为 66066123⋯363。如果一个整数从左到右读出所有数位上的数字, 与从右到左读出所有数位 上的数字是一样的, 则称这个数为回文数。例如…...

FinOps首次超越安全成为企业头等大事|云计算趋势报告
随着云计算在过去十年中的广泛应用,云计算用户所面临的一个持续不变的趋势是:安全一直是用户面临的首要挑战。然而,这种情况正在发生转变。 知名IT软件企业 Flexera 对云计算决策者进行年度调研已经持续12年,而今年安全问题首次…...

【深度强化学习】(3) Policy Gradients 模型解析,附Pytorch完整代码
大家好,今天和各位分享一下基于策略的深度强化学习方法,策略梯度法是对策略进行建模,然后通过梯度上升更新策略网络的参数。我们使用了 OpenAI 的 gym 库,基于策略梯度法完成了一个小游戏。完整代码可以从我的 GitHub 中获得&…...

Windows基于Nginx搭建RTMP流媒体服务器(附带所有组件下载地址及验证方法)
RTMP服务时常用于直播时提供拉流推流传输数据的一种服务。前段时间由于朋友想搭建一套直播时提供稳定数据传输的服务器,所以就研究了一下如何搭建及使用。 1、下载nginx 首先我们要知道一般nginx不能直接配置rtmp服务,在Windows系统上需要特殊nginx版本…...

交流电机驱动器中的隔离电压感应
汽车和工业终端设备,如电机驱动器、串式逆变器和机载充电器,在高电压下运行,不能安全地与人直接互动。隔离电压测量通过保护人类免受高压电路执行一个功能的影响,有助于优化操作和确保使用的安全性。 设计用于高性能,隔…...
爬取知乎问题答案
参考博客:基于Python知乎回答爬虫 jieba关键字统计可视化_知乎爬虫搜索关键词_菠萝柚王子的博客-CSDN博客 1、安装依赖包 import numpy import requests import certifi from PIL import Image from lxml import etree import jieba from wordcloud import WordClo…...
通用智能理论
将智能定义为解决矛盾的能力,用解决矛盾的概率提升来评估智能程度,以此为基础推导智能原理,建立一种新的通用智能理论。 1 前言 通用人工智能(Artificial General Intelligence)是人类长久以来的梦想。经历了一次次挫败…...

保姆级使用PyTorch训练与评估自己的MixMIM网络教程
文章目录前言0. 环境搭建&快速开始1. 数据集制作1.1 标签文件制作1.2 数据集划分1.3 数据集信息文件制作2. 修改参数文件3. 训练4. 评估5. 其他教程前言 项目地址:https://github.com/Fafa-DL/Awesome-Backbones 操作教程:https://www.bilibili.co…...

《百万在线 大型游戏服务端开发》前两章概念笔记
第1章 从角色走路说起 游戏网络通信的流程则是服务端先开启监听,等待客户端的连接,然后交互操作,最后断开。 套接字 每个Socket都包含网络连接中一端的信息。每个客户端需要一个Socket结构,服务端则需要N1个Socket结构ÿ…...

3BHE029110R0111 ABB
3BHE029110R0111 ABB变频器控制方式低压通用变频输出电压为380~650V,输出功率为0.75~400kW,工作频率为0~400Hz,它的主电路都采用交—直—交电路。其控制方式经历了以下四代。1U/fC的正弦脉宽调制࿰…...
实现防重复操作(JS与CSS)
实现防重复操作(JS与CSS) 一、前言 日常开发中我们经常会对按钮进行一个防重复点击的校验,这个通常使用节流函数来实现。在规定时间内只允许提交一次,可以有效的避免事件过于频繁的执行和重复提交操作,以及为服务器考…...

怎么合并或注销重复LinkedIn领英帐号?
您可能会发现您拥有多个领英帐户。如果您收到消息,提示您尝试使用的邮箱与另一个帐户已绑定,就表明您可能存在重复的领英帐户。如果您使用许多不同的邮箱地址,也可能会收到这样的提示。 领英精灵温馨提示: 目前,仅支持在 PC 端合并…...

Redis高频面试题汇总(中)
目录 1.什么是redis事务? 2.如何使用 Redis 事务? 3.Redis 事务为什么不支持原子性 4.Redis 事务支持持久性吗 5.Redis事务基于lua脚本的实现 6.Redis集群的主从复制模型是怎样的? 7.Redis集群中,主从复制的数据同步的步骤 …...
web vue 项目 Docker化部署
Web 项目 Docker 化部署详细教程 目录 Web 项目 Docker 化部署概述Dockerfile 详解 构建阶段生产阶段 构建和运行 Docker 镜像 1. Web 项目 Docker 化部署概述 Docker 化部署的主要步骤分为以下几个阶段: 构建阶段(Build Stage):…...

【力扣数据库知识手册笔记】索引
索引 索引的优缺点 优点1. 通过创建唯一性索引,可以保证数据库表中每一行数据的唯一性。2. 可以加快数据的检索速度(创建索引的主要原因)。3. 可以加速表和表之间的连接,实现数据的参考完整性。4. 可以在查询过程中,…...

LeetCode - 394. 字符串解码
题目 394. 字符串解码 - 力扣(LeetCode) 思路 使用两个栈:一个存储重复次数,一个存储字符串 遍历输入字符串: 数字处理:遇到数字时,累积计算重复次数左括号处理:保存当前状态&a…...

Vue2 第一节_Vue2上手_插值表达式{{}}_访问数据和修改数据_Vue开发者工具
文章目录 1.Vue2上手-如何创建一个Vue实例,进行初始化渲染2. 插值表达式{{}}3. 访问数据和修改数据4. vue响应式5. Vue开发者工具--方便调试 1.Vue2上手-如何创建一个Vue实例,进行初始化渲染 准备容器引包创建Vue实例 new Vue()指定配置项 ->渲染数据 准备一个容器,例如: …...

2021-03-15 iview一些问题
1.iview 在使用tree组件时,发现没有set类的方法,只有get,那么要改变tree值,只能遍历treeData,递归修改treeData的checked,发现无法更改,原因在于check模式下,子元素的勾选状态跟父节…...

Python爬虫(一):爬虫伪装
一、网站防爬机制概述 在当今互联网环境中,具有一定规模或盈利性质的网站几乎都实施了各种防爬措施。这些措施主要分为两大类: 身份验证机制:直接将未经授权的爬虫阻挡在外反爬技术体系:通过各种技术手段增加爬虫获取数据的难度…...
linux 下常用变更-8
1、删除普通用户 查询用户初始UID和GIDls -l /home/ ###家目录中查看UID cat /etc/group ###此文件查看GID删除用户1.编辑文件 /etc/passwd 找到对应的行,YW343:x:0:0::/home/YW343:/bin/bash 2.将标红的位置修改为用户对应初始UID和GID: YW3…...

均衡后的SNRSINR
本文主要摘自参考文献中的前两篇,相关文献中经常会出现MIMO检测后的SINR不过一直没有找到相关数学推到过程,其中文献[1]中给出了相关原理在此仅做记录。 1. 系统模型 复信道模型 n t n_t nt 根发送天线, n r n_r nr 根接收天线的 MIMO 系…...
docker 部署发现spring.profiles.active 问题
报错: org.springframework.boot.context.config.InvalidConfigDataPropertyException: Property spring.profiles.active imported from location class path resource [application-test.yml] is invalid in a profile specific resource [origin: class path re…...

GruntJS-前端自动化任务运行器从入门到实战
Grunt 完全指南:从入门到实战 一、Grunt 是什么? Grunt是一个基于 Node.js 的前端自动化任务运行器,主要用于自动化执行项目开发中重复性高的任务,例如文件压缩、代码编译、语法检查、单元测试、文件合并等。通过配置简洁的任务…...