首先直接给出答案:不是线程安全的
一、分析问题
证明不是线程安全的案例如下:
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();}
}