多线程02

简介: 多线程02

1.线程安全问题及解决.

1.1线程安全问题介绍.

  • 当我们使用多个线程访问同一资源(可以是同一个变量、同一个文件、同一条记录等)的时候,若多个线程只有读操作,那么不会发生线程安全问题。但是如果多个线程中对资源有读和的操作,就容易出现线程安全问题。
  • 举例:


1.2线程安全问题案例演示-卖票.

  • 案例:火车站要卖票,我们模拟火车站的卖票过程。因为疫情期间,本次列车的座位共100个(即,只能出售100张火车票)。我们来模拟车站的售票窗口,实现多个窗口同时售票的过程。注意:不能出现错票、重票。
  • 分析,采用哪种线程实现方式?
  • 分析一:多个窗口(线程)共同卖100张票,也就是多个线程做同样的一件事情:卖这100张票。线程和线程之间数据是共享的,采用实现Runnable接口方式实现线程。
  • 分析二:采用继承Thread类方式也可以实现,需要在线程类里面把票的总量定义成静态的全局变量:static int piao = 100.
  • 通过实现Runnable接口方式实现该案例:

  • 运行结果如下:

  • 从结果上可以发现,出现了一票多卖的情况,这是多条线程在同一时间访问同一资源的原因造成的【资源访问冲突】,也就是线程安全问题。

1.3同步机制解决线程安全问题.

  • 线程同步,即:排队。
  • 要解决上述多线程并发访问一个资源的安全性问题:也就是解决重复票与不存在票问题,Java中提供了同步机制(synchronized)来解决。如下图所示:

  • 窗口1线程进入操作的时候,窗口2和窗口3线程只能在外等着,窗口1操作结束,窗口1和窗口2和窗口3才有机会进入代码去执行。也就是说在某个线程修改共享资源的时候,其他线程不能修改该资源,等待修改完毕同步之后,才能去抢夺CPU资源,完成对应的操作,保证了数据的同步性,解决了线程不安全的现象。
  • 为了保证每个线程都能正常执行原子操作,Java引入了线程同步机制。注意:在任何时候,最多允许一个线程拥有同步锁,谁拿到锁就进入代码块,其他的线程只能在外等着(blocked:封锁的/闭塞的/阻塞/堵塞)。

1.4同步机制解决线程安全问题的原理.

  • 同步机制的原理,其实就相当于给某段代码加“锁”,任何线程想要执行这段代码,都要先获得“锁”,我们称它为同步锁。
  • 因为Java对象在堆中的数据分为分为对象头、实例变量、空白的填充。而对象头中包含:
  1. Mark Word:记录了和当前对象有关的GC、锁标记等信息。
  2. 指向类的指针:每一个对象需要记录它是由哪个类创建出来的。
  3. 数组长度(只有数组对象才有)。
  • 哪个线程获得了“同步锁”对象之后,”同步锁“对象就会记录这个线程的ID,这样其他线程就只能等待了,除非这个线程”释放“了锁对象,其他线程才能重新获得/占用“同步锁”对象。

1.5同步代码块和同步方法.

1.5.1同步代码块.

  • 同步代码块:synchronized 关键字可以用于某个区块前面,表示只对这个区块的资源实行互斥访问。
  • 语法如下:

  1. 同步锁:里的参数是同步监视器,俗称“同步锁”。同步锁必须一个唯一的对象(可以是常量,但不能是静态的常量),多条线程必须公用一把锁。如何判断对象是唯一的?比如可以在Runnable匿名实现类里面定义一个 Object obj = new Object();对象,然后 obj对象就可以作为同步锁。对象obj是唯一的前提条件是,obj对象所在的类只被实例化了一次,如果实例化多次,那么 obj对象就不是唯一的了。如果为了方便,不想再重新定义一个唯一的对象,可以采用 this对象作为同步锁,this对象代表调用当前方法的对象。
  2. 被同步的代码:需要被同步的代码指的是,操作共享数据的代码;
  3. 共享数据是:多个线程共同操作的变量。
  • 通过同步代码块解决卖票案例问题:

  • 注1:while循环不能被锁。分析:如果while循环也被锁了,那么第一个拿到锁的线程就会不停的卖票,直到把票卖完,这样100张票就会被第一个抢到锁的线程卖完(代码验证)。
  • 注2:这里的同步锁 this代表调用当前 run方法的对象,也就是 runnable对象,该对象只被创建了一个,是唯一的,满足同步锁的要求。

1.5.2同步方法.

  • 同步方法:synchronized 关键字直接修饰方法,表示同一时刻只有一个线程能进入这个方法,其他线程在外面等着。
  • synchronized 锁住的方法,同步锁是 this对象,this对象代表调用当前方法的对象。
  • 语法如下:

  • 通过同步方法方式解决卖票案例问题:

  • 测试


相关文章
|
缓存 NoSQL 安全
【Redis系列笔记】缓存三剑客
缓存穿透是指请求一个不存在的数据,缓存层和数据库层都没有这个数据,这种请求会穿透缓存直接到数据库进行查询。它通常发生在一些恶意用户可能故意发起不存在的请求,试图让系统陷入这种情况,以耗尽数据库连接资源或者造成性能问题。 缓存击穿发生在访问热点数据,大量请求访问同一个热点数据,当热点数据失效后同时去请求数据库,瞬间耗尽数据库资源,导致数据库无法使用。 缓存雪崩是缓存中大量key失效后当高并发到来时导致大量请求到数据库,瞬间耗尽数据库资源,导致数据库无法使用。
390 2
|
9月前
|
NoSQL 关系型数据库 MySQL
招行面试:高并发写,为什么不推荐关系数据?
资深架构师尼恩针对高并发场景下为何不推荐使用关系数据库进行数据写入进行了深入剖析。文章详细解释了关系数据库(如MySQL)在高并发写入时的性能瓶颈,包括存储机制和事务特性带来的开销,并对比了NoSQL数据库的优势。通过具体案例和理论分析,尼恩为读者提供了系统化的解答,帮助面试者更好地应对类似问题,提升技术实力。此外,尼恩还分享了多个高并发系统的解决方案及优化技巧,助力开发者在面试中脱颖而出。 文章链接:[原文链接](https://mp.weixin.qq.com/s/PKsa-7eZqXDg3tpgJKCAAw) 更多技术资料和面试宝典可关注【技术自由圈】获取。
|
存储 监控 NoSQL
|
11月前
|
监控 Serverless 数据库
探索 Serverless 架构:云计算的新浪潮
【10月更文挑战第23天】Serverless 架构是一种新兴的云计算范式,允许开发者构建和运行应用程序而无需管理服务器。本文深入探讨了 Serverless 的核心概念、优势、挑战及最佳实践,帮助开发者更好地理解和应用这一技术。
|
JSON 数据格式
DTHttpJson UE4插件使用说明
DTHttpJson UE4插件使用说明
748 0
|
12月前
|
缓存 Java 程序员
Java|SpringBoot 项目开发时,让 FreeMarker 文件编辑后自动更新
在开发过程中,FreeMarker 文件编辑后,每次都需要重启应用才能看到效果,效率非常低下。通过一些配置后,可以让它们免重启自动更新。
232 0
|
存储 监控 Python
python 检测文件大小并定期删除
python 检测文件大小并定期删除
168 1
|
前端开发 JavaScript
uniapp 创建组件
uniapp 创建组件
157 0
uniapp 创建组件
|
Linux
Linux安装OpenSSL
Linux安装OpenSSL
248 0