线程并发安全问题认知强化
导致线程不安全的因素有哪些?
如何保证并发线程的安全性?
如何理解JAVA中的悲观锁和乐观锁?
如何理解线程的上下文切换?
如何理解死锁以及避免死锁问题?
线程通讯与进程通讯应用增强
如何实现进程内部线程之间的通讯?
基于Condition实现
如何实现进程之间间通讯(IPC)?
总结(Summary)
===================================================================
进程:操作系统进行资源调度和分配的基本单位(例如浏览器,APP,JVM)。
线程:进程中的最小执行单位(可以理解为一个顺序的执行流)。
说明:同一个进程内的多个线程共享资源。
并发:多线程抢占CPU,可能不同时执行,侧重于多个任务交替执行。
现在的操作系统无论是windows,linux还是macOS等其实都是多用户多任务分时操作系统,使用这些操作系统的的用户可以“同时”干多件事情。但实际上,对于单机CPU的计算机而言,在同一时间只能干一件事,为了看起来像是“同时干多件事”分时操作系统把CPU的时间划分成了长短进本相同的时间区间,即“时间片”,通过操作系统的管理,把时间片依次轮流的分配给各个线程任务使用。我们看似的“同时干多件事”,其实是通过CPU时间片技术并发完成的。例如:多个线程并发使用一个CPU资源并发执行任务的线程时序图。
并行:线程可以不共享CPU,可每个线程一个CPU同时执行多个任务。
总之:个人认为并行只出现在多CPU或多核CPU中,而并发可理解为并行中的一个子集。
一个线程从创建,运行,到最后销毁的这个过程称之为线程的生命周期,在这个生命周期过程中线程可能会经历如下几个状态:
这些状态可归纳为:状态分别为新建状态,就绪状态,运行状态,阻塞状态,死亡状态。
======================================================================
多个线程并发执行时,仍旧能够保证数据的正确性,这种现象称之为线程安全。
多个线程并发执行时,不能能够保证数据的正确性,这种现象称之为线程不安全。
案例分享:如何保证12306中的订票操作的安全。
第一步:编写售票任务类:
class TicketTask implements Runnable{
int ticket=10;
@Override
public void run() {
doTicket();
}
public void doTicket() {
while(true) {
if(ticket<=0)break;
System.out.println(ticket–);
}
}
}
第二步:编写售票测试方法:
public static void main(String[] args) {
TicketTask task=new TicketTask();
Thread t1=new Thread(task);
Thread t2=new Thread(task);
Thread t3=new Thread(task);
t1.start();
t2.start();
t3.start();
}
多个线程并发执行。
多个线程并发执行时存在共享数据集(临界资源)。
多个线程在共享数据集上的操作不是原子操作。
例如:现有一生产者消费者模型,生产者和消费者并发操作容器对象。
第一;对共享进行限制访问(例如加锁:syncronized,Lock)
第二:基于CAS实现非阻塞同步(基于CPU硬件技术支持)
第三:取消共享,每个线程一个对象实例(例如threadlocal)
说明:Java中的线程安全问题的主要关注点有3个:可见性,有序性,原子性;
Java内存模型(JMM)解决了可见性和有序性问题,而解决了原子性问题。
JAVA中为了保证多线程并发访问的安全性,提供了基于锁的应用,大体可归纳为两大类,即悲观锁和乐观锁。
悲观锁&乐观锁定义说明:
1)悲观锁:假定会发生并发冲突,屏蔽一切可违反数据完整性的操作,例如java中可以基于syncronized,Lock,ReadWriteLock等实现。
悲观锁&乐观锁应用场景说明:
1)悲观锁适合写操作多的场景,先加锁可以保证写操作时数据正确。
2)乐观锁适合读操作多的场景,不加锁的特点能够使其读操作的性能大幅提升
悲观锁&乐观锁应用案例分析
悲观锁实现计数器:
方案1:
class Counter{
private int count;
public synchronized int count() {
count++;
return count;
}
}
方案2:
class Counter{
private int count;
private Lock lock=new ReentrantLock();
public int count() {
lock.lock();
try {
count++;
return count;
}finally {
lock.unlock();
}
}
}
乐观锁实现计数器:
class Counter{
private AtomicInteger at=new AtomicInteger();
public int count() {
return at.incrementAndGet();
}
}
其中 AtomicInteger 是基于CAS算法实现。
一个线程得到CPU执行的时间是有限的。当此线程用完为其分配的CPU时间以后,cpu会切换到下一个线程执行。但是在这之前,线程需要将当前的状态进行保存,以便下次再次获得CPU时间片时可以加载对应的状态以继续执行剩下的任务。而这个切换过程是需要耗费时间的,会影响多线程程序的执行效率,所以在在使用多线程时要减少线程的频繁切换。那如何实现呢?
减少多线程上下文切换的方案如下:
无锁并发编程:锁的竞争会带来线程上下文的切换
CAS算法:CAS算法在数据更新方面,可以达到锁的效果
使用最少线程:避免不必要的线程等待
使用协程:单线程完成多任务的调度和切换,避免多线程
多个线程互相等待已经被对方线程正在占用的锁,导致陷入彼此等待对方释放锁的状态,这个过程称之为死锁。
如何避免死锁呢?
避免一个线程中同时获取多个锁
避免一个线程在一个锁中获取其他的锁资源
考虑使用定时锁来替换内部锁机制,如lock.tryLock(timeout)。
可能出现死锁的案例分享
class SyncThread implements Runnable {
private Object obj1;
private Object obj2;
public SyncThread(Object o1, Object o2) {
this.obj1 = o1;
this.obj2 = o2;
}
@Override
public void run() {
synchronized (obj1) {
work();
synchronized (obj2) {
work();
}
}
}
private void work() {
try {Thread.sleep(30000);} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
死锁测试
public class TestDeadLock01 {
public static void main(String[] args)throws Exception {
Object obj1 = new Object();
Object obj2 = new Object();
Thread t1 = new Thread(new SyncThread(obj1, obj2), “t1”);
Thread t2 = new Thread(new SyncThread(obj2, obj1), “t2”);
t1.start();
t2.start();
}
}
=======================================================================
线程通讯:java中的多线程通讯主要是共享内存(变量)等方式。
进程通讯:java中进程通讯(IPC)主要是Socket,MQ等。
wait()/notify()/notifyall()方法定义说明:
Wait:阻塞正在使用监视器对象的线程,同时释放监视器对象
notify: 唤醒在监视器对象上等待的单个线程,但不释放监视器对象,此时调用该方法的代码继续执行,直到执行结束才释放对象锁
notifyAll: 唤醒在监视器对象上等待的所有线程,但不释放监视器对象,此时调用该方法的代码继续执行,直到执行结束才释放对象锁
wait()/notify()/notifyall()方法应用说明
1) 这些方法必须应用在同步代码块或同步方法中
2) 这些方法必须由监视器对象(对象锁)调用
说明:使用wait/notify/notifyAll的作用一般是为了避免轮询带来的性能损失。
wait()/notify()/notifyall()应用案例实现:
手动实现阻塞式队列,并基于wait()/notifyAll()方法实现实现线程在队列上的通讯。
/**
有界消息队列:用于存取消息
1)数据结构:数组(线性结构)
2)具体算法:FIFO(先进先出)-First in First out
*/
public class BlockContainer {//类泛型
/*用于存储数据的数组/
private Object[] array;
/*记录有效元素个数/
private int size;
public BlockContainer () {
this(16);//this(参数列表)表示调用本类指定参数的构造函数
}
public BlockContainer (int cap) {
array=new Object[cap];//每个元素默认值为null
}
}
向容器添加put方法,用于放数据。
/**
生产者线程通过put方法向容器放数据
数据永远放在size位置
说明:实例方法内部的this永远指向
调用此方法的当前对象(当前实例)
注意:静态方法中没有this,this只能
应用在实例方法,构造方法,实例代码块中
*/
public synchronized void put(T t){//同步锁:this
//1.判定容器是否已满,满了则等待
while(size==array.length)
try{this.wait();}catch(Exception e){}
//2.放数据
array[size]=t;
//3.有效元素个数加1
size++;
//4.通知消费者取数据
this.notifyAll();
}
向容器类添加take方法,用于从容器取数据。
/**
消费者通过此方法取数据
位置:永远取下标为0的位置的数据
@return
*/
@SuppressWarnings(“unchecked”)
public synchronized T take(){
//1.判定容器是否为空,空则等待
while(size==0)
try{this.wait();}catch(Exception e){}
//2.取数据
Object obj=array[0];
//3.移动元素
System.arraycopy(
array,//src 原数组
1, //srcPos 从哪个位置开始拷贝
array, //dest 放到哪个数组
0, //destPost 从哪个位置开始放
size-1);//拷贝几个
//4.有效元素个数减1
size–;
//5.将size位置为null
array[size]=null;
//6.通知生产者放数据
this.notifyAll();//通知具备相同锁对象正在wait线程
return (T)obj;
}
Condition 类定义说明
Condition 是一个用于多线程间协同的工具类,基于此类可以方便的对持有锁的线程进行阻塞或唤醒阻塞的线程。它的强大之处在于它可以为多个线程间建立不同的Condition,通过signal()/signalall()方法指定要唤醒的不同线程。
Condition 类应用说明
基于Lock对象获取Condition对象
基于Condition对象的await()/signal()/signalall()方法实现线程阻塞或唤醒。
Condition类对象的应用案例实现:
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数Java工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,真正体系化!
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!
如果你觉得这些内容对你有帮助,可以扫码获取!!(备注Java获取)
由于细节内容实在太多了,为了不影响文章的观赏性,只截出了一部分知识点大致的介绍一下,每个小节点里面都有更细化的内容!
小编准备了一份Java进阶学习路线图(Xmind)以及来年金三银四必备的一份《Java面试必备指南》
《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!
-1713452093904)]
[外链图片转存中…(img-Da4qJICe-1713452093907)]
[外链图片转存中…(img-omCezxIs-1713452093909)]
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,真正体系化!
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!
如果你觉得这些内容对你有帮助,可以扫码获取!!(备注Java获取)
由于细节内容实在太多了,为了不影响文章的观赏性,只截出了一部分知识点大致的介绍一下,每个小节点里面都有更细化的内容!
[外链图片转存中…(img-snP3bSpC-1713452093911)]
小编准备了一份Java进阶学习路线图(Xmind)以及来年金三银四必备的一份《Java面试必备指南》
[外链图片转存中…(img-GJzAraGj-1713452093912)]
《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!
文章浏览阅读3.2k次。本文研究全球与中国市场分布式光纤传感器的发展现状及未来发展趋势,分别从生产和消费的角度分析分布式光纤传感器的主要生产地区、主要消费地区以及主要的生产商。重点分析全球与中国市场的主要厂商产品特点、产品规格、不同规格产品的价格、产量、产值及全球和中国市场主要生产商的市场份额。主要生产商包括:FISO TechnologiesBrugg KabelSensor HighwayOmnisensAFL GlobalQinetiQ GroupLockheed MartinOSENSA Innovati_预计2026年中国分布式传感器市场规模有多大
文章浏览阅读1.1k次,点赞2次,收藏12次。常用组合逻辑电路结构——为IC设计的延时估计铺垫学习目的:估计模块间的delay,确保写的代码的timing 综合能给到多少HZ,以满足需求!_基4布斯算法代码
文章浏览阅读3.3k次,点赞3次,收藏5次。OpenAI Manager助手(基于SpringBoot和Vue)_chatgpt网页版
文章浏览阅读2.2k次。USACO自1992年举办,到目前为止已经举办了27届,目的是为了帮助美国信息学国家队选拔IOI的队员,目前逐渐发展为全球热门的线上赛事,成为美国大学申请条件下,含金量相当高的官方竞赛。USACO的比赛成绩可以助力计算机专业留学,越来越多的学生进入了康奈尔,麻省理工,普林斯顿,哈佛和耶鲁等大学,这些同学的共同点是他们都参加了美国计算机科学竞赛(USACO),并且取得过非常好的成绩。适合参赛人群USACO适合国内在读学生有意向申请美国大学的或者想锻炼自己编程能力的同学,高三学生也可以参加12月的第_usaco可以多次提交吗
文章浏览阅读394次。1.1 存储程序1.2 创建存储过程1.3 创建自定义函数1.3.1 示例1.4 自定义函数和存储过程的区别1.5 变量的使用1.6 定义条件和处理程序1.6.1 定义条件1.6.1.1 示例1.6.2 定义处理程序1.6.2.1 示例1.7 光标的使用1.7.1 声明光标1.7.2 打开光标1.7.3 使用光标1.7.4 关闭光标1.8 流程控制的使用1.8.1 IF语句1.8.2 CASE语句1.8.3 LOOP语句1.8.4 LEAVE语句1.8.5 ITERATE语句1.8.6 REPEAT语句。_mysql自定义函数和存储过程
文章浏览阅读188次。半导体二极管——集成电路最小组成单元。_本征半导体电流为0
文章浏览阅读2.8k次,点赞3次,收藏18次。游戏水面特效实现方式太多。咱们这边介绍的是一最简单的UV动画(无顶点位移),整个mesh由4个顶点构成。实现了水面效果(左图),不动代码稍微修改下参数和贴图可以实现岩浆效果(右图)。有要思路是1,uv按时间去做正弦波移动2,在1的基础上加个凹凸图混合uv3,在1、2的基础上加个水流方向4,加上对雾效的支持,如没必要请自行删除雾效代码(把包含fog的几行代码删除)S..._unity 岩浆shader
文章浏览阅读5k次。广义线性模型是线性模型的扩展,它通过连接函数建立响应变量的数学期望值与线性组合的预测变量之间的关系。广义线性模型拟合的形式为:其中g(μY)是条件均值的函数(称为连接函数)。另外,你可放松Y为正态分布的假设,改为Y 服从指数分布族中的一种分布即可。设定好连接函数和概率分布后,便可以通过最大似然估计的多次迭代推导出各参数值。在大部分情况下,线性模型就可以通过一系列连续型或类别型预测变量来预测正态分布的响应变量的工作。但是,有时候我们要进行非正态因变量的分析,例如:(1)类别型.._广义线性回归模型
文章浏览阅读69次。环境保护、 保护地球、 校园环保、垃圾分类、绿色家园、等网站的设计与制作。 总结了一些学生网页制作的经验:一般的网页需要融入以下知识点:div+css布局、浮动、定位、高级css、表格、表单及验证、js轮播图、音频 视频 Flash的应用、ul li、下拉导航栏、鼠标划过效果等知识点,网页的风格主题也很全面:如爱好、风景、校园、美食、动漫、游戏、咖啡、音乐、家乡、电影、名人、商城以及个人主页等主题,学生、新手可参考下方页面的布局和设计和HTML源码(有用点赞△) 一套A+的网_垃圾分类网页设计目标怎么写
文章浏览阅读614次,点赞7次,收藏11次。之前找到一个修改 exe 中 DLL地址 的方法, 不太好使,虽然能正确启动, 但无法改变 exe 的工作目录,这就影响了.Net 中很多获取 exe 执行目录来拼接的地址 ( 相对路径 ),比如 wwwroot 和 代码中相对目录还有一些复制到目录的普通文件 等等,它们的地址都会指向原来 exe 的目录, 而不是自定义的 “lib” 目录,根本原因就是没有修改 exe 的工作目录这次来搞一个启动程序,把 .net 的所有东西都放在一个文件夹,在文件夹同级的目录制作一个 exe._.net dll 全局目录
文章浏览阅读1.5k次。本文为转载,原博客地址:http://blog.csdn.net/hujingshuang/article/details/46910259简介 BRIEF是2010年的一篇名为《BRIEF:Binary Robust Independent Elementary Features》的文章中提出,BRIEF是对已检测到的特征点进行描述,它是一种二进制编码的描述子,摈弃了利用区域灰度..._breif description calculation 特征点
文章浏览阅读4.1k次,点赞21次,收藏79次。本文是《基于SpringBoot的房屋租赁管理系统》的配套原创说明文档,可以给应届毕业生提供格式撰写参考,也可以给开发类似系统的朋友们提供功能业务设计思路。_基于spring boot的房屋租赁系统论文