为什么JAVA开发大多数时候不需要管线程安全的问题?

20 篇文章 6 订阅

 

作为一名JAVA开发,虽然平时学习了很多关于线程安全的问题,但是你有没有思考这样一个问题:为什么平时开发过程中我们根本不需要管线程安全的问题呢?

我们的后台程序是同时面向很多用户的,通过Tomcat线程池,给每个访问的用户分配一个线程去执行我们的程序。既然是这样,那不就意味着同样的数据同时被多个线程访问或修改啦?线程安全是怎么保证的呢?为什么我们平时开发的时候几乎不需要考虑这样普遍存在的线程安全问题呢?当你有这样的疑问的时候,说明你已经开始自己独立思考了,而不是照本宣科,别人怎么说你也怎么说。

前提知识:

线程安全的定义:同一份数据被多个线程同时修改造成脏数据的产生。线程不安全的前提:同一个数据同时被多个线程所修改。

Spring中有两种Bean,第一种是被Spring管理的Bean,这种Bean初始化时默认是单例的,也就是说一个JVM里面A类只会产生一个A对象,比如被@Controller,@Service,@Component修饰的类;第二种Bean是不被Spring管理的Bean,比如你创建的DO,VO等实体类。

而我们平时写的代码都遵循了以下规范:

1、所有@Controller,@Service,@Component修饰的类的所有成员变量必须得被@Autowired,@Resource等修饰。这意味着这个类的所有成员变量也都是被Spring管理的Bean,也都是单例的,这就造成了这个类是无状态的,就是说这个类它本身没有保存任何有效数据!而且层层嵌套下去每一个这样的类都是无状态的!

2、实体类不被Spring管理,有效数据都是被封装到实体类里面的。每次new实体类都会新生成一个持有有效数据的对象,而且new实体类都是发生在方法里面,这就意味着这个对象的引用是线程私有的!

接下来我们以线程的角度来看一下,一个写操作的从前到后的流程,看看是否会发生线程不安全的情况:

用户访问进来,Tomcat给A用户分配A线程,给B用户分配B线程。A用户访问Controller中一个write方法,在这个方法中new了一个H实体类,生成了一个h对象,这个h对象的的引用被放在A线程的栈上(局部变量放在栈上,栈内存是线程私有的),所以能访问到h对象的线程只有A线程。同样B线程访问Controller中的write方法,也在这个方法中new了一个H实体类,生成一个hh对象,这个hh对象的引用被放在了B线程的栈上,能访问hh对象的线程只有B线程。所以你看到了:A线程和B线程虽然执行了同样的方法,执行了同样的代码流程,但是他们持有的数据是完全隔离的!这就是为什么我们程序中不需要考虑线程安全的原因,因为线程不安全的前提条件都没有。

然后A线程将h对象保存入库,B线程也在同一时间将hh对象保存入库,当两个这样的事务同时抵达Mysql数据库的时候,就是Mysql数据库的事务特性起作用的时候,ACID,通过事务之间的隔离性(可提交读、不可提交读、可重复读、序列化)来保证最后数据的一致性。哈哈,最后我们把并发的复杂性交给了Mysql来处理!而我们程序中通过简单地保持上面的规范,成功地避免了并发带来的线程安全问题,只不过因为每个线程都需要持有一份独立的数据,需要耗费JVM堆内存,尤其是并发量高的时候。

 

 

 

 

 

 

 

 

 

 

 

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值