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

java 线程的移动问题_Spring Boot中的多线程问题和ThreadLocal

1、Spring中的多线程疑惑

首先我们需要认清:

web容器本身就是多线程的,每一个HTTP请求都会产生一个独立的线程(或者从线程池中取得创建好的线程);

Spring中的bean(用@Repository、@Service、@Component和@Controller注册的bean)都是单例的,即整个程序、所有线程共享一个实例;

虽然bean都是单例的,但是Spring提供的模板类(XXXTemplate),在Spring容器的管理下(使用@Autowired注入),会自动使用ThreadLocal以实现多线程;

即类是单例的,但是其中有可能出现并发问题的变量使用ThreadLocal实现了多线程。

注意除了Spring本身提供的类以外,在Bean中定义“有状态的变量”(即有存储数据的变量),其会被所有线程共享,很可能导致并发问题,需要自行另外使用ThreadLocal进行处理,或者将Bean声明为prototype型。

一个类中的方法实际上是独立,方法内定义的局部变量在每次调用方法时都是独立的,不会有并发问题。只有类的“有状态的”全局变量会有并发问题

结论:

使用Spring提供的template等类没有多线程问题!

一般来说只有类的属性/全局变量会导致多线程问题,而方法内的局部变量不会有并发问题

单例模式肯定是线程不安全的! spring的Bean中的自定义的成员变量除非进行threadlocal封装,否则都是非线程安全的!

2、Spring中的prototype和@Autowired

2.1、使用@Autowired没有实现多个实例

注意即使使用@Scope(BeanDefinition.SCOPE_PROTOTYPE)将Bean声明为prototype,如果:

外层的类是singleton

使用@Autowired注入

这样的话仍然只会有一个实例。例如:

使用@Scope(BeanDefinition.SCOPE_PROTOTYPE)声明的Bean

@Component

@Scope(BeanDefinition.SCOPE_PROTOTYPE)

public class ClassB {

private Integer num = Integer.valueOf(0);

public Integer getNum() {

return num;

}

public void setNum(Integer num) {

this.num = num;

}

}

使用@Autowired注入到ClassA中

/**

* Created by ASUS on 2017/5/24.

*/

@Component

public class ClassA {

@Autowired

private ClassB classB;

public Integer addNum(){

classB.setNum(classB.getNum()+1);

System.out.println(classB.getNum());

return classB.getNum();

}

}

通过Controller调用,用@Autowire将ClassA注入。

@RestController

public class Controller {

@Autowired

private ClassA classA;

@RequestMapping("/")

public String print(){

return classA.addNum().toString();

}

}

每一次访问都会导致num+1,访问8次后:

93572e6e4934a33e6c1eb5380de4f561.png

并没有实现“多个实例”的效果,一次都是在操作同一个ClassB实例。这是因为使用@Autowired实际上和直接new是一个效果,只是交由Spring容器实现而已。而ClassA本身是一个单例,单例只会实例化一次,这样其属性自然也就只会被实例化一次。

2.2、解决方法

2.2.1、直接在方法中声明局部变量

在Java或者其他语言中,每个“方法”在被调用时,都会重新声明一遍方法中的局部变量。如果想要classB为多实例,直接在方法中声明即可。比如:

@Component

@Scope(BeanDefinition.SCOPE_PROTOTYPE)

public class ClassA {

public Integer addNum(){

//不使用@Autowired,直接在方法中声明

ClassB classB = new ClassB();

classB.setNum(classB.getNum()+1);

System.out.println(classB.getNum());

return classB.getNum();

}

}

注意:

这样做的问题在于,如果ClassB中需要使用@Autowired,则这个@Autowired会失效。

比如想在ClassB中使用Spring管理的JdbcTemplate,就需要使用@Autowired。如果ClassB不是通过@Autowired实例化的,ClassB中的JdbcTemplate就会注入失败,导致NullPointerException。

2.2.2、使用ThreadLocal管理属性

还要一个方法实现和多实例“类似”的功能,即使用ThreadLocal来管理类中的属性。例如对于上面的例子:

@Component

@Scope(BeanDefinition.SCOPE_PROTOTYPE)

public class ClassB {

//使用ThreadLocal管理属性,每个线程都操作一个新的副本

private static ThreadLocal integerThreadLocal = new ThreadLocal(){

@Override

protected Integer initialValue() {

return 0;

}

};

public ThreadLocal getIntegerThreadLocal() {

return integerThreadLocal;

}

}

在ClassB中就可以正常使用@Autowired进行注入。

@Component

@Scope(BeanDefinition.SCOPE_PROTOTYPE)

public class ClassA {

@Autowired

ClassB classB;

public Integer addNum(){

Integer integer = classB.getIntegerThreadLocal().get();

integer++;

return integer;

}

}

相关文章:

  • Java 经常用到access_用Java连接到Microsoft Access 2007数据库的正确方法是什么?
  • java1.8 interface_JDK1.8新特性——FunctionInterface
  • php file_get_contents 中文,php file_get_contents函数怎么用
  • php 平均下载速度,php限制下载速度的实现方法
  • docker lamp php7,环境准备:docker-compose安装 LAMP、LNMP、php扩展
  • java system.in 怎么写,java 里System.in 输入流如何使用
  • php 两数最大相同子串,用javascript求两个字符串最大的相同的子串(代码实例)...
  • JAVA ulimit,Linux:使用ulimit设置文件最大打开数
  • matlab表示数据散度的统计量,matlab kl-divergence(KL散度)实现代码 | 学步园
  • mysql anzhaung xiangjie,GitHub - dizhaung/spring-boot-student: spring-boot-student
  • php 2m 上传 限制,解决wordpress上传文件2M限制
  • matlab画图的参数,Matlab 画图plot参数 颜色 类型
  • 谱聚类算法 matlab,SpectralClustering 谱聚类算法的matlab实现 238万源代码下载- www.pudn.com...
  • centos 查看php并发访问量,如何查看centos是否联网
  • java编程实现求素数个数,Java并行程序设计——求素数个数(Runnable接口实现)...
  • @angular/forms 源码解析之双向绑定
  • Angular2开发踩坑系列-生产环境编译
  • CentOS7 安装JDK
  • iBatis和MyBatis在使用ResultMap对应关系时的区别
  • Java编程基础24——递归练习
  • java正则表式的使用
  • KMP算法及优化
  • overflow: hidden IE7无效
  • React-redux的原理以及使用
  • springMvc学习笔记(2)
  • 更好理解的面向对象的Javascript 1 —— 动态类型和多态
  • 精益 React 学习指南 (Lean React)- 1.5 React 与 DOM
  • 如何正确配置 Ubuntu 14.04 服务器?
  • 设计模式 开闭原则
  • 应用生命周期终极 DevOps 工具包
  • 终端用户监控:真实用户监控还是模拟监控?
  • #!/usr/bin/python与#!/usr/bin/env python的区别
  • $.type 怎么精确判断对象类型的 --(源码学习2)
  • ()、[]、{}、(())、[[]]命令替换
  • (C++17) std算法之执行策略 execution
  • (java版)排序算法----【冒泡,选择,插入,希尔,快速排序,归并排序,基数排序】超详细~~
  • (Matlab)基于蝙蝠算法实现电力系统经济调度
  • (ZT)出版业改革:该死的死,该生的生
  • (每日持续更新)jdk api之FileReader基础、应用、实战
  • (三)elasticsearch 源码之启动流程分析
  • (实战)静默dbca安装创建数据库 --参数说明+举例
  • (心得)获取一个数二进制序列中所有的偶数位和奇数位, 分别输出二进制序列。
  • .Net MVC4 上传大文件,并保存表单
  • .net mvc部分视图
  • .NET 使用配置文件
  • .net 验证控件和javaScript的冲突问题
  • .netcore 获取appsettings
  • .net中我喜欢的两种验证码
  • @DependsOn:解析 Spring 中的依赖关系之艺术
  • @开发者,一文搞懂什么是 C# 计时器!
  • [ Algorithm ] N次方算法 N Square 动态规划解决
  • [ Linux ] git工具的基本使用(仓库的构建,提交)
  • [].shift.call( arguments ) 和 [].slice.call( arguments )
  • []AT 指令 收发短信和GPRS上网 SIM508/548
  • [AIGC] Kong:一个强大的 API 网关和服务平台