引述要学习Spring框架的技术内幕,必须事先掌握一些基本的Java知识,正所谓“登高必自卑,涉远必自迩”。以下几项Java知识和Spring框架息息相关,不可不学(我将通过一个系列分别介绍这些Java基础知识,希望对大家有所帮助。):
[1] Java反射知识-->Spring IoC :http://www.iteye.com/topic/1123081
[2] Java动态代理-->Spring AOP :http://www.iteye.com/topic/1123293
[3] 属性编辑器,即PropertyEditor-->Spring IoC:http://www.iteye.com/topic/1123628
[4] XML基础知识-->Spring配置:http://www.iteye.com/topic/1123630
[5] 注解-->Spring配置:http://www.iteye.com/topic/1123823
[6] 线程本地变更,即ThreadLocal-->Spring事务管理:http://www.iteye.com/topic/1123824
[7] 事务基础知识-->Spring事务管理:http://www.iteye.com/topic/1124043
[8] 国际化信息-->MVC:http://www.iteye.com/topic/1124044
[9] HTTP报文-->MVC:http://www.iteye.com/topic/1124408
我们知道Spring通过各种模板类降低了开发者使用各种数据持久技术的难度。这些模板类都是线程安全的,也就是说,多个DAO可以复用同一个模板实例而不会发生冲突。我们使用模板类访问底层数据,根据持久化技术的不同,模板类需要绑定数据连接或会话的资源。但这些资源本身是非线程安全的,也就是说它们不能在同一时刻被多个线程共享。虽然模板类通过资源池获取数据连接或会话,但资源池本身解决的是数据连接或会话的缓存问题,并非数据连接或会话的线程安全问题。
按照传统经验,如果某个对象是非线程安全的,在多线程环境下,对对象的访问必须采用synchronized进行线程同步。但模板类并未采用线程同步机制,因为线程同步会降低并发性,影响系统性能。此外,通过代码同步解决线程安全的挑战性很大,可能会增强好几倍的实现难度。那么模板类究竟仰仗何种魔法神功,可以在无须线程同步的情况下就化解线程安全的难题呢?答案就是ThreadLocal!
ThreadLocal在Spring中发挥着重要的作用,在管理request作用域的Bean、事务管理、任务调度、AOP等模块都出现了它们的身影,起着举足轻重的作用。要想了解Spring事务管理的底层技术,ThreadLocal是必须攻克的山头堡垒。
ThreadLocal是什么
早在JDK 1.2的版本中就提供java.lang.ThreadLocal,ThreadLocal为解决多线程程序的并发问题提供了一种新的思路。使用这个工具类可以很简洁地编写出优美的多线程程序。
ThreadLocal,顾名思义,它不是一个线程,而是线程的一个本地化对象。当工作于多线程中的对象使用ThreadLocal维护变量时,ThreadLocal为每个使用该变量的线程分配一个独立的变量副本。所以每一个线程都可以独立地改变自己的副本,而不会影响其他线程所对应的副本。从线程的角度看,这个变量就像是线程的本地变量,这也是类名中“Local”所要表达的意思。
线程局部变量并不是Java的新发明,很多语言(如IBM XL、FORTRAN)在语法层面就提供线程局部变量。在Java中没有提供语言级支持,而以一种变通的方法,通过ThreadLocal的类提供支持。所以,在Java中编写线程局部变量的代码相对来说要笨拙一些,这也是为什么线程局部变量没有在Java开发者中得到很好普及的原因。
ThreadLocal的接口方法
ThreadLocal类接口很简单,只有4个方法,我们先来了解一下。
- void set(Object value) 设置当前线程的线程局部变量的值;
- public Object get() 该方法返回当前线程所对应的线程局部变量;
- public void remove() 将当前线程局部变量的值删除,目的是为了减少内存的占用,该方法是JDK 5.0新增的方法。需要指出的是,当线程结束后,对应该线程的局部变量将自动被垃圾回收,所以显式调用该方法清除线程的局部变量并不是必须的操作,但它可以加快内存回收的速度;
- protected Object initialValue() 返回该线程局部变量的初始值,该方法是一个protected的方法,显然是为了让子类覆盖而设计的。这个方法是一个延迟调用方法,在线程第1次调用get()或set(Object)时才执行,并且仅执行1次。ThreadLocal中的默认实现直接返回一个null。
值得一提的是,在JDK5.0中,ThreadLocal已经支持泛型,该类的类名已经变为ThreadLocal<T>。API方法也相应进行了调整,新版本的API方法分别是void set(T value)、T get()以及T initialValue()。
ThreadLocal是如何做到为每一个线程维护变量的副本的呢?其实实现的思路很简单:在ThreadLocal类中有一个Map,用于存储每一个线程的变量副本,Map中元素的键为线程对象,而值对应线程的变量副本。我们自己就可以提供一个简单的实现版本:
代码清单9-3 SimpleThreadLocal
public class SimpleThreadLocal { private Map valueMap = Collections.synchronizedMap(new HashMap()); public void set(Object newValue) { //①键为线程对象,值为本线程的变量副本 valueMap.put(Thread.currentThread(), newValue); } public Object get() { Thread currentThread = Thread.currentThread(); //②返回本线程对应的变量 Object o = valueMap.get(currentThread); //③如果在Map中不存在,放到Map中保存起来 if (o == null && !valueMap.containsKey(currentThread)) { o = initialValue(); valueMap.put(currentThread, o); } return o; } public void remove() { valueMap.remove(Thread.currentThread()); } public Object initialValue() { return null; } }
虽然代码清单9 3中这个ThreadLocal实现版本显得比较幼稚,但它和JDK所提供的ThreadLocal类在实现思路上是非常相近的。
一个TheadLocal实例
下面,我们通过一个具体的实例了解一下ThreadLocal的具体使用方法。
代码清单9-4 SequenceNumber
package com.baobaotao.basic; public class SequenceNumber { //①通过匿名内部类覆盖ThreadLocal的initialValue()方法,指定初始值 private static ThreadLocal<Integer> seqNum = new ThreadLocal<Integer>(){ public Integer initialValue(){ return 0; } }; //②获取下一个序列值 public int getNextNum(){ seqNum.set(seqNum.get()+1); return seqNum.get(); } public static void main(String[ ] args) { SequenceNumber sn = new SequenceNumber(); //③ 3个线程共享sn,各自产生序列号 TestClient t1 = new TestClient(sn); TestClient t2 = new TestClient(sn); TestClient t3 = new TestClient(sn); t1.start(); t2.start(); t3.start(); } private static class TestClient extends Thread { private SequenceNumber sn; public TestClient(SequenceNumber sn) { this.sn = sn; } public void run() { //④每个线程打出3个序列值 for (int i = 0; i < 3; i++) { System.out.println("thread["+Thread.currentThread().getName()+ "] sn["+sn.getNextNum()+"]"); } } } }
通常我们通过匿名内部类的方式定义ThreadLocal的子类,提供初始的变量值,如①处所示。TestClient线程产生一组序列号,在③处,我们生成3个TestClient,它们共享同一个SequenceNumber实例。运行以上代码,在控制台上输出以下的结果:
thread[Thread-0] sn[1]
thread[Thread-1] sn[1]
thread[Thread-2] sn[2]
thread[Thread-0] sn[2]
thread[Thread-1] sn[2]
thread[Thread-2] sn[3]
thread[Thread-0] sn[3]
thread[Thread-1] sn[3]
考查输出的结果信息,我们发现每个线程所产生的序号虽然都共享同一个Sequence Number实例,但它们并没有发生相互干扰的情况,而是各自产生独立的序列号,这是因为我们通过ThreadLocal为每一个线程提供了单独的副本。
与Thread同步机制的比较
ThreadLocal和线程同步机制相比有什么优势呢?ThreadLocal和线程同步机制都是为了解决多线程中相同变量的访问冲突问题。
在同步机制中,通过对象的锁机制保证同一时间只有一个线程访问变量。这时该变量是多个线程共享的,使用同步机制要求程序缜密地分析什么时候对变量进行读写,什么时候需要锁定某个对象,什么时候释放对象锁等繁杂的问题,程序设计和编写难度相对较大。
而ThreadLocal则从另一个角度来解决多线程的并发访问。ThreadLocal为每一个线程提供一个独立的变量副本,从而隔离了多个线程对访问数据的冲突。因为每一个线程都拥有自己的变量副本,从而也就没有必要对该变量进行同步了。ThreadLocal提供了线程安全的对象封装,在编写多线程代码时,可以把不安全的变量封装进ThreadLocal。
由于ThreadLocal中可以持有任何类型的对象,低版本JDK所提供的get()返回的是Object对象,需要强制类型转换。但JDK 5.0通过泛型很好的解决了这个问题,在一定程度上简化ThreadLocal的使用,代码清单9-2就使用了JDK 5.0新的ThreadLocal<T>版本。
概括起来说,对于多线程资源共享的问题,同步机制采用了“以时间换空间”的方式:访问串行化,对象共享化。而ThreadLocal采用了“以空间换时间”的方式:访问并行化,对象独享化。前者仅提供一份变量,让不同的线程排队访问,而后者为每一个线程都提供了一份变量,因此可以同时访问而互不影响。
Spring使用ThreadLocal解决线程安全问题
我们知道在一般情况下,只有无状态的Bean才可以在多线程环境下共享,在Spring中,绝大部分Bean都可以声明为singleton作用域。就是因为Spring对一些Bean(如RequestContextHolder、TransactionSynchronizationManager、LocaleContextHolder等)中非线程安全的“状态性对象”采用ThreadLocal进行封装,让它们也成为线程安全的“状态性对象”,因此有状态的Bean就能够以singleton的方式在多线程中正常工作了。
一般的Web应用划分为展现层、服务层和持久层三个层次,在不同的层中编写对应的逻辑,下层通过接口向上层开放功能调用。在一般情况下,从接收请求到返回响应所经过的所有程序调用都同属于一个线程,如图9-2所示。
这样用户就可以根据需要,将一些非线程安全的变量以ThreadLocal存放,在同一次请求响应的调用线程中,所有对象所访问的同一ThreadLocal变量都是当前线程所绑定的。
下面的实例能够体现Spring对有状态Bean的改造思路:
代码清单9-5 TopicDao:非线程安全
public class TopicDao { //①一个非线程安全的变量 private Connection conn; public void addTopic(){ //②引用非线程安全变量 Statement stat = conn.createStatement(); … } }
由于①处的conn是成员变量,因为addTopic()方法是非线程安全的,必须在使用时创建一个新TopicDao实例(非singleton)。下面使用ThreadLocal对conn这个非线程安全的“状态”进行改造:
代码清单9-6 TopicDao:线程安全
import java.sql.Connection; import java.sql.Statement; public class TopicDao { //①使用ThreadLocal保存Connection变量 private static ThreadLocal<Connection> connThreadLocal = new ThreadLocal<Connection>(); public static Connection getConnection(){ //②如果connThreadLocal没有本线程对应的Connection创建一个新的Connection, //并将其保存到线程本地变量中。 if (connThreadLocal.get() == null) { Connection conn = ConnectionManager.getConnection(); connThreadLocal.set(conn); return conn; }else{ //③直接返回线程本地变量 return connThreadLocal.get(); } } public void addTopic() { //④从ThreadLocal中获取线程对应的 Statement stat = getConnection().createStatement(); } }
不同的线程在使用TopicDao时,先判断connThreadLocal.get()是否为null,如果为null,则说明当前线程还没有对应的Connection对象,这时创建一个Connection对象并添加到本地线程变量中;如果不为null,则说明当前的线程已经拥有了Connection对象,直接使用就可以了。这样,就保证了不同的线程使用线程相关的Connection,而不会使用其他线程的Connection。因此,这个TopicDao就可以做到singleton共享了。
当然,这个例子本身很粗糙,将Connection的ThreadLocal直接放在Dao只能做到本Dao的多个方法共享Connection时不发生线程安全问题,但无法和其他Dao共用同一个Connection,要做到同一事务多Dao共享同一个Connection,必须在一个共同的外部类使用ThreadLocal保存Connection。但这个实例基本上说明了Spring对有状态类线程安全化的解决思路。在本章后面的内容中,我们将详细说明Spring如何通过ThreadLocal解决事务管理的问题。
相关推荐
球类物体检测系统源码分享[一条龙教学YOLOV8标注好的数据集一键训练_70+全套改进创新点发刊_Web前端展示]
交通信号灯检测系统源码分享
基站设备检测系统源码分享[一条龙教学YOLOV8标注好的数据集一键训练_70+全套改进创新点发刊_Web前端展示]
人脸活体检测系统源码分享[一条龙教学YOLOV8标注好的数据集一键训练_70+全套改进创新点发刊_Web前端展示]
这款资源系统是一款基于PHP和MySQL开发的内容管理系统(CMS),广泛应用于资源分享、下载站点的搭建。该系统以简洁、高效、易用为特点,适合快速构建资源类网站。 核心功能 资源管理:支持多种资源类型(如软件、文档、视频等)的上传、分类、展示和下载。 用户系统:提供用户注册、登录、权限管理等功能,支持用户积分、等级制度。 SEO优化:内置SEO功能,支持自定义URL、关键词、描述等,提升搜索引擎排名。 模板管理:支持多套模板切换,用户可以根据需求自定义网站外观。 插件扩展:系统支持插件机制,用户可以通过安装插件扩展功能,如支付接口、社交分享等。 安全机制:内置防SQL注入、XSS攻击等安全机制,保障系统安全。 适用场景: 资源下载站 软件分享平台 文档分享站点 视频资源站
内容: 本研究探讨了巴西北部一个红树林边缘河口中的溶解有机物(DOM)动态,将DOM组成与其形成地点的氧化还原条件联系起来。通过结合分子分析与营养盐和微量元素数据,我们强调了难降解DOM的外流作为沿海碳储存的重要贡献者,并提出了一种新颖的分子指数(ISuP),用于区分复杂沿海生态系统中的DOM来源。该数据集包括由超高质量分辨率质谱(傅里叶变换离子回旋共振质谱仪,FT-ICR-MS)获得的溶解有机物的分子数据、从FT-ICR-MS数据计算出的分子指数(ISuP 和 ITerr),以及环境数据,包括溶解有机碳(DOC)、营养盐(硝酸盐和磷酸盐)和微量元素(铁、锰、钡)的数据。水样采集自巴西北部帕拉州布拉甘萨附近的一个红树林边缘河口。此研究对于理解沿海生态系统中碳循环及其在全球变化背景下所扮演的角色具有重要意义。"访问数据集" ()以获取更多详情。
2024免费毕业设计成品,包括源码+数据库+往届论文资料 启动教程:https://www.bilibili.com/video/BV11ktveuE2d 讲解视频:https://www.bilibili.com/video/BV1YfkHYwEME 二次开发教程:https://www.bilibili.com/video/BV1Cw2rY1ErC
2024免费毕业设计成品,包括源码+数据库+往届论文资料 启动教程:https://www.bilibili.com/video/BV11ktveuE2d 讲解视频:https://www.bilibili.com/video/BV1YfkHYwEME 二次开发教程:https://www.bilibili.com/video/BV1Cw2rY1ErC
火车车厢检测系统源码分享
智慧社区的建设背景与需求 智慧社区的建设源于“互联网+”时代的呼唤,是业主刚需促成的社区变革。随着市场化进程的加速,传统社区面临着运营业务少、建设成本高、维护难度大、业务不精、增值服务少、无数据沉淀、运营模式单一等问题。而新技术如大数据、云计算的崛起,为人与人、人与物、物与物之间的无界限连接提供了可能,推动了智慧社区的发展。业主对于智能家居、可视对讲、智能安防、社区消费、在线物业、社区互动等体验式社区的需求,也成为了购房的刚需。智慧社区的建设,旨在通过一站式服务提升楼盘品质及品牌溢价,简化物业系统,增强管理效率,降低建设及维护成本,为业主提供便捷、舒适的生活服务,并转型为服务提供商。 智慧社区的核心子系统与功能 智慧社区的建设依赖于多个核心子系统,包括视频监控、可视对讲、一卡通、背景音乐、信息发布等。视频监控子系统提供了全方位的安全保障,通过密码加密传输、数据库安全、云存储等技术,实现了录像的安全存储和智能分析,如全景监控、人员异常活动检测等功能。可视对讲子系统不仅实现了基本的对讲功能,还加入了人脸识别、远程开门、信息发布等智能化功能。一卡通子系统涵盖了门禁、考勤、消费、访客、梯控、巡更等多个应用场景,实现了统一数据库和身份认证体系下的便捷管理。此外,背景音乐子系统提供了定时广播、实时广播、事件联动等功能,而信息发布子系统则支持文字、图片、即时和任务播放,以及分组管理,为社区内的信息传播提供了便利。智慧社区还注重家居的智能化,通过情景模式预设、一键自动控制、系统传感器和逻辑功能自动运行等任务,以及兼容常规电器设备,为业主提供了舒适、健康、便利的居住环境。 智慧社区的运营方案与未来展望 智慧社区的运营方案包括开放的云平台、智能终端和丰富应用,旨在打造智慧社区行业生态圈。云平台的建设实现了海量信息存储、强大的计算能力,以及统一部署、统一服务、统一用户体验和降低成本的目标。交互客服平台的打造,通过公司门户网站、小区客服网站、业主个人中心等多渠道,建立了物业与业主之间的信任关系。智慧社区还提供了多方位的多媒体广告,搭建了用户与经营者之间的桥梁,增加了物业收入。同时,智慧社区还注重公私车位运营等增值业务,通过手机APP下单、确认租赁等方式,实现了车位的有效利用和物业收入的增加。未来,智慧社区将继续深化智能化建设,拓展更多应用场景,为业主提供更加便捷、舒适、智能的生活体验。
棒球运动物体检测系统源码分享[一条龙教学YOLOV8标注好的数据集一键训练_70+全套改进创新点发刊_Web前端展示]
2024免费毕业设计成品,包括源码+数据库+往届论文资料 启动教程:https://www.bilibili.com/video/BV11ktveuE2d 讲解视频:https://www.bilibili.com/video/BV1YfkHYwEME 二次开发教程:https://www.bilibili.com/video/BV1Cw2rY1ErC
去年写的学生点名系统,使用c#的xaml做的动画,使用账密登录,支持背景图修改,读取姓名,点名倒计时,背景音乐,手动停止,速度调整等。
二维码与条形码检测系统源码分享[一条龙教学YOLOV8标注好的数据集一键训练_70+全套改进创新点发刊_Web前端展示]
内容概要:文章主要介绍了利用决策树和朴素贝叶斯算法对鸢尾花进行分类的研究过程。文中首先概述了研究背景和意义,指出了鸢尾花数据集作为经典机器学习数据集的重要性,以及通过此数据集可以帮助理解和优化算法性能。研究内容涵盖了算法的基本原理、技术细节,如信息熵、信息增益及其比率,还包括对模型进行剪枝、性能评估等多项步骤。作者通过一系列实验证明,这两类方法能够在不同程度上有效地分辨三种不同品种的鸢尾花,并针对各自的优势与局限性给出了具体的分析与改进建议。 适用人群:适用于正在接触或学习机器学习入门级别的学生以及相关技术人员,尤其是那些希望加深对于分类算法尤其是决策树与朴素贝叶斯这两种经典算法了解的人群。 使用场景及目标:该研究旨在通过对鸢尾花数据集的实际操作,让学生或从业者掌握决策树和朴素贝叶斯在实际案例中的构建方法。同时培养他们对分类问题建模的兴趣和技能,提高他们在选择适当算法应对不同类型问题的能力。 其他说明:除了详细讲解两个核心主题外,文档还提及了一些关于数据处理(包括但不限于预处理和特征工程)、实验配置以及结果解读方面的基础知识。这对于初学者来说是非常有用的参考资料。值得注意的是,虽然文中强调决策树算法的优点,但也提到了诸如过拟合之类的潜在缺陷,并提出了相应的解决方案。总的来说,本文不仅有助于读者建立起对于两类主流分类算法的理解,也为未来的研究工作奠定了坚实的基础。
模拟军事目标检测系统源码分享[一条龙教学YOLOV8标注好的数据集一键训练_70+全套改进创新点发刊_Web前端展示]
内容: 该研究探讨了三种典型水样中的溶解有机物(DOM)的独特色谱行为,这些水样分别代表了沿海DOM、海洋表面DOM和海洋难降解DOM。在RV Polarstern的ANT XXII/2航次期间(站位PS67/006-130,纬度-67.5633,经度-55.3448),使用采水器从威德尔海表面(30米深度,海洋表面DOM)和深水(1356米深度,难降解DOM)采集水样,并在其他地方有所描述(El Naggar等人,2007;Koch等人,2008)。实验过程中,将160升海水通过0.2微米滤芯过滤,酸化至pH 2,并泵入60毫升固相萃取柱(PPL,5克)。DOM用40毫升甲醇洗脱后,在-18°C下保存。沿海DOM通常从南北海(纬度54.1447,经度7.8711)提取,并作为实验室内部标准使用。海水经过0.2微米PTFE(Whatman)过滤,酸化至pH 2后,也采用PPL萃取柱进行处理。 数据集包含4组数据,详细信息可访问提供的链接获取。
5f3074e9b14c8a0069729d6464d15e35.PNG
花卉识别系统源码分享
重点:所有项目均附赠详尽的SQL文件,这一细节的处理,让我们的项目相比其他博主的作品,严谨性提升了不止一个量级!更重要的是,所有项目源码均经过我亲自的严格测试与验证,确保能够无障碍地正常运行。 1.项目适用场景:本项目特别适用于计算机领域的毕业设计课题、课程作业等场合。对于计算机科学与技术等相关专业的学生而言,这些项目无疑是一个绝佳的选择,既能满足学术要求,又能锻炼实际操作能力。 2.超值福利:所有定价为9.9元的项目,均包含完整的SQL文件。如需远程部署可随时联系我,我将竭诚为您提供满意的服务。在此,也想对一直以来支持我的朋友们表示由衷的感谢,你们的支持是我不断前行的动力! 3.求关注:如果觉得我的项目对你有帮助,请别忘了点个关注哦!你的支持对我意义重大,也是我持续分享优质资源的动力源泉。再次感谢大家的支持与厚爱! 4.资源详情:https://blog.csdn.net/2301_78888169/article/details/141651888 更多关于项目的详细信息与精彩内容,请访问我的CSDN博客!