线程安全-线程同步
1.什么是线程安全:
java允许多线程并发控制,当多个线程同时操作一个可共享的资源变量时(如对同一个数据的增删改查),将会导致数据不准确,相互之间产生冲突,因此加入同步锁以避免在该线程没有完成操作之前,被其他线程的调用,从而保证了该变量的唯一性和准确性。
二、 如何使用同步?
在网上简单的搜了一下,发现同步的方式有5-7种,其实同步来同步去,里面的原理是没有变化的,那么今天就简单来讲一种:
1、用 synchronized 关键字修饰方法、代码块
实现线程安全:synchronized
(1)方法加锁
public synchronized void a(){
//在该方法中可以访问共享的对象
}
(2)代码块加锁
public void b(){
synchronized(共享对象){
i++;
}
}
注:同步是一种高开销的操作,因此应该尽量减少同步的内容。 通常没有必要同步整个方法,使用synchronized代码块同步关键代码即可。
三、如上所说,我们就接着上一篇的卖票系统来增加多个窗口卖票,也就是增加多个线程,来演示一下线程的同步:
(一)演示背景:假设我们有一个卖票系统,共有50张票,原来只有一个窗口,现在因为近期生意火爆,顾客排队等待时间较长,所以现在要增加三个窗口,那么写到代码中就是增加三个线程(一个窗口代表卖票的一个线程,那选在就一共有四个线程)
(二)代码的类与方法与上一篇的一样,不一样的就是在测试阶段,增加了三个线程,代码如下:
2)票的类
/**
*@ClassName Tickets
*@Description TODD
*@AUTHOR sh-wangbs
*@Date 2019/2/279:55
*@Version 1.0
**/
//该注解省去了 get set 方法
@Data
public class Tickets {
//票的总数
private int count;
//有参构造
public Tickets(int count) {
super();
this.count = count;
}
//无参构造
public Tickets() {
super();
}
}
2)实现Runnable接口的卖票的类
**
*@ClassName SaleTicketsbyRannable
*@Description TODD
*@AUTHOR sh-wangbs
*@Date 2019/2/279:55
*@Version 1.0
**/
@Data
public class SaleTicketsbyRannable implements Runnable {
//获得票的类
public Tickets tic;
//有参构造
public SaleTicketsbyRannable(Tickets tic) {
super();
this.tic = tic;
}
@Override
public void run() {
while (tic.getCount()>0){
sale();
}
}
//卖票的方法(这里的synchronized 关键字起这关键的作用,就是给该方法加锁)
public synchronized void sale() {
//获取当前线程的名字,直观的看出是哪个线程
String threadname = Thread.currentThread().getName();
//如果票数大于零卖票
if(tic.getCount()>0){
System.out.println(threadname+":第"+tic.getCount()+"张票已售出!");
//卖票后总票数减1
tic.setCount(tic.getCount()-1);
try {
//线程沉睡0.2秒,只是方便看演示效果
Thread.currentThread().sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
}else{
//当票数为0是输出“票已售空"
tic.setCount(tic.getCount());
System.out.println(threadname+"的票已售空!");
}
}
}
3)演示五个窗口卖票结果的测试类
/***
* @Description: 卖票测试类
* @Param:
* @return:
* @author: wangbs
* @create: 2019/2/27 11:06
*/
public class TestSaleTickets {
public static void main(String[] args) {
//票的实体类,有100张票
Tickets tic = new Tickets(100);
//创建一个多线程
SaleTicketsbyRannable str = new SaleTicketsbyRannable(tic);
//开启五个线程也就是五个卖票窗口
Thread thread1 = new Thread(str,"卖票窗口1");
Thread thread2 = new Thread(str,"卖票窗口2");
Thread thread3 = new Thread(str,"卖票窗口3");
Thread thread4 = new Thread(str,"卖票窗口4");
Thread thread5 = new Thread(str,"卖票窗口5");
//执行
thread1.start();
thread2.start();
thread3.start();
thread4.start();
}
}
4)演示效果
由结果可知,五个卖票的窗口,同时开始售票,但是在内部机制里还是一个一个的 来执行的,里面的执行是无序的,买次售票都需要对应的线程抢占资源,才能卖票,也就是说,哪个线程先获得资源,哪个线程就先执行,到售完票以后,若再次访问窗口,则会提示“票已售完”的信息!这就是线程之间的同步!
(下一篇我会讲下线程间的通信问题)
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构