当前位置: 首页 > news >正文

短信登录和登录校验(线程安全、ThreadLocal、进程调度)

在做黑马点评项目的登录验证时,老师说用户信息可能会有线程安全问题,需要使用ThreadLocal,我当时就不太理解了,用户信息存储在session中怎么会有线程安全问题呢?

1.什么是线程:

每次客户端向服务器发送request请求,一个HTTP请求到达服务器,Tomcat会从线程池中分配一个线程来执行这个请求,这个线程会经过拦截器、控制器、服务层、数据访问层等,直到返回响应。

2.session获取用户信息的缺点:

首先,使用session并不会出现线程安全问题,因为session是用户级别,不同用户之间隔离,但是使用session有缺点:

Class Service1{void method11(user){}void method12(user){}Class Service2{void method21(user){}void method22(user){}	
}

如上所示,当有很多方法都需要user时,那么这些方法的参数必然都需要定义一个user或session对象,这就很冗余了,不仅类内的方法冗余,不同类之间也冗余。

3.如何解决:

这时候就有同学问了,把user单独设置成一个全局变量就不好了:

方法1:

User user;Class Service1{void method11(){user = new User(name, age);}void method12(){}Class Service2{void method21(){}void method22(){}	
}

这样确实对于类内的方法和不同类之间完美的解决了冗余问题,但会出现线程安全问题,而且线程安全问题是最严重的。

方法2:

Class Service1{User user;void method11(){user = new User(name, age);}void method12(){不用new,解耦了}Class Service2{User user;void method21(){user = new User(name, age);}void method22(){不用new,解耦了	}	
}

这种方法能减少类内的冗余,但是不同类之间还是需要重复定义User user,类间冗余依然没有改善,并且也有线程安全问题。

3.什么是线程安全问题:

举个例子,有一个方法用来创建订单:

Class OrderService{User user;void createOrder(){user = new User(name, age);orderMapper.insert(user);}

首先需要知道,Controller、Service和Component都是单例模式,使用IOC自动注入,也就是说在容器中只有一个实例,所有用户的线程(request)共用这一个对象。

如果同时有两个用户执行createOrder方法,user1刚执行new User()给user赋值,user2就抢占了CPU执行new User()。由于OrderService对象是单例,那么此时user中存的是user2的信息。user1获得CPU后执行orderMapper.insert(user)使用的是user2的信息,就会出错,这就是线程安全问题。

进程同步解决线程安全问题:

这时候学过操作系统的的宝宝看到 “抢占,调度” 是不是感觉似曾相识,就能想到了:如果把new User()和createOrder()定义原子操作,不能被其他线程抢占不就行了?

是的,这就是操作系统的进程(线程)同步机制。啥?进程同步里面的内容都忘了怎么办。那么我给几个提示:PV操作、互斥锁、条件变量。想起来没有?(虽然当时感觉没用吧,现在一结合实际问题,就觉得没白学)

PV操作即P(wait)和V(signal)操作,用于管理临界区的互斥访问和进程间的同步,例如哲学家就餐问题就是PV。在数据库事务的ACID特性中,PV操作主要与隔离性类似(但是不要混淆进程同步和事务,他们一个针对的是Service层的线程安全问题,一个针对的是Mapper层的数据脏读、不可重复读问题)

我们通常提到数据库事务时,都会想到ACID属性,ACID是指原子性(Atomicity)、一致性(Consistency)、隔离性(Isolation)和持久性(Durability)。下面分别解释:

原子性(Atomicity):事务是一个不可分割的工作单位,事务中的操作要么都发生,要么都不发生。例如,银行转账操作中,从A账户扣款和向B账户加款必须同时成功或同时失败。如果中途发生错误,则会回滚到事务开始前的状态。

一致性(Consistency):事务必须使数据库从一个一致性状态变换到另一个一致性状态。也就是说,事务执行前后,数据库都必须处于一致性状态。例如,转账前后两个账户的总金额应该保持不变。

隔离性(Isolation):多个用户并发访问数据库时,数据库为每个用户开启的事务,不能被其他事务的操作数据所干扰,多个并发事务之间要相互隔离。隔离级别有读未提交、读已提交、可重复读、串行化等。

持久性(Durability):一旦事务提交,则其对数据库的更改就是永久性的。即使系统发生故障,也不会丢失提交的事务。

如下所示,通过PV操作确保进程(线程)对临界区的串行访问,解决了并发环境下的资源竞争和协作问题

Class OrderService{User user;void createOrder(){P()user = new User(name, age);orderMapper.insert(user);V()}

但是其实PV操作复杂了,所以实际开发一般用不到,反而是锁机制用的比较多。

锁机制解决线程安全问题:

这个后面的业务中会学到乐观锁、悲观锁…

ThreadLocal解决线程安全问题:

ThreadLocal就相当于这种方法:

User user;Class Service1{void method11(){user = new User(name, age);}void method12(){}Class Service2{void method21(){}void method22(){}	
}

线程(request请求)不再共享同一个user,而是每个线程分别分配一个user对象,既确保了不同线程之间的user隔离,又解决了线程安全问题。

这里留一个问号,既然这样的话后面为什么还要用到乐观锁呢?直接用Tread不就行了

我的推测是,类似于超卖问题:

Class OrderService{void Sail(user){检查库存;创建订单;扣减库存;}

这里user使用形参或者ThreadLocal实际上并没有线程安全问题,但是Sail()方法本身执行的这三个操作可能会有线程安全问题,这里我还没想到怎么会出问题,等后面项目再理解吧。

http://www.dtcms.com/a/565928.html

相关文章:

  • 旅游网站html快速网站收录
  • 视频网站开发用什么服务器门户网站登录入口
  • python虚拟环境应用
  • SpringBoot-启动流程
  • 余姚公司建设网站海东高端网站建设价格
  • C# OpencvSharp使用lpd_yunet进行车牌检测
  • 淘宝联盟登记新网站广州网站建设公司怎么选
  • 济南天桥区网站建设公司豪华大气的旅行社网站源码
  • 网络通信的奥秘:网络层ip与路由详解(四)
  • Spring 框架介绍
  • 物联网设备物理环境自适应监控与运维策略优化
  • Redis 简介与安装指南
  • 营销网站建设专业服务公司精准大数据营销公司
  • 同性做视频网站wordpress 制作支付页
  • 公司内部网站建设方案最简单的网站建设
  • Linux 系统的内存分布结构及其之间的关系(持续更新)
  • DeviceNet转ProfiNet边缘计算网关赋能:西门子 1200PLC 与库卡机器人通讯配置完整案例
  • 网络卡顿运维排查方案:从客户端到服务器的全链路处理
  • 成都网站seo公司网站优化报告
  • 聊城网站制作价格做名片的网站
  • 辽宁网站建设招标网站如何做百度推广方案
  • ECharts 实战:`connectNulls` 的妙用——绘制连续折线图并跳过 0 值节点
  • Mysql引擎
  • 报表类系统后端API设计思路
  • 谷歌的技术栈是什么?
  • Token 存储与安全防护
  • HAProxy 简介及配置
  • 电商系统网站建设网站客户端制作教程
  • 只会后端不会前端如何做网站免费wordpress页面编辑器
  • BIRGMA验厂要求