首先直接给出答案:不是线程安全的
一、分析问题
证明不是线程安全的案例如下:
public class Student {
private String stuName;
public String report(String uname){
stuName = "大家好,我叫:"+uname;
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
return stuName;
}
}
-----------------------------------------------------------------------------------------------------------------
public class Run {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MainConfig.class);
Student bean1 = context.getBean(Student.class);
new Thread(() -> {
System.out.println(bean1.report("张三"));
}).start();
Student bean2 = context.getBean(Student.class);
new Thread(() -> {
System.out.println(bean2.report("李四"));
}).start();
}
}
分析原因:线程一执行完stuName的赋值后进入休眠,线程二这时候也进入该方法对stuName进行赋值,由于对象是单例的,线程二的赋值操作也就影响了线程一的打印结果。导致最后打印的结果都是线程二传入的值。
二、解决方法
既然单例bean不是线程安全的,那么该怎么解决上面的问题呢?下面博主给出四种解决方法仅供读者参考:
1.方法一:将成员变量放入方法中
修改后的Student类如下:
public class Student {
// private String stuName;
public String report(String uname){
String stuName = "大家好,我叫:"+uname;
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
return stuName;
}
}
2.方法二:加锁使方法串行执行
比如下面的方法中我加入了synchronized锁:
public class Student {
private String stuName;
public synchronized String report(String uname){
stuName = "大家好,我叫:"+uname;
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
return stuName;
}
}
3.方法三:将bean变成原型模式
比如加上Scope注解声明为多例模式:
@Bean
@Scope("prototype")
public Student student(){
return new Student();
}
4.方法四:使用ThreadLocal
改造后的代码如下:
public class Student {
private ThreadLocal<String> stuName = new ThreadLocal<>();
public String report(String uname){
stuName.set("大家好,我叫:"+uname);
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
return stuName.get();
}
}