目录
2、新建外部类添加成员变量(这个是教程3中用到的,我比较常用)
一、引言
多线程数据隔离与数据共享是个矛盾体,有些数据需要隔离,比如每个人的银行账户,有些需要共享比如买票的总火车票数量,这个问题导致了这篇文章的出现,抽象出了这两类问题。
二、数据隔离
多线程之间就是因为数据共享在多个线程才导致了线程不安全,这就要求线程间的数据需要隔离,从根本上解决了线程安全问题,比如我们每个人都有自己的银行账户,每次存钱行为一样,但是都存入了自己的银行账户而不是通用的银行账户。
这个问题ThreadLocal变量帮我们解决了这个问题,我们只需要创建该变量,在每个线程里面使用相当于它在每个线程里面都有一个相同名字的变量供我们使用。
public class TestThreadlocal {
static ThreadLocal<Student> threadLocalst=new ThreadLocal<Student>();
public static void main(String [] args)
{
//System.out.print("asdf");
for (int i=0;i<10;i++)
{
new Thread(new Runnable() {
public void run() {
Student student=new Student();
student.setAge(new Random().nextInt() +"");
student.setName("xcy");
System.out.println(Thread.currentThread().getName()+ student.getName()+student.getAge());
threadLocalst.set(student);
new A().get();
new B().get();
}
}){}.start();
}
}
static class A{
public void get (){
Student st=threadLocalst.get();
System.out.println(Thread.currentThread().getName()+ st.getName()+st.getAge());
}
}
static class B{
public void get (){
Student st=threadLocalst.get();
System.out.println(Thread.currentThread().getName()+ st.getName()+st.getAge());
}
}
}
student类
public class Student {
private String name;
private String age;
private boolean flag;
public boolean isFlag() {
return flag;
}
public void setFlag(boolean flag) {
this.flag = flag;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAge() {
return age;
}
public void setAge(String age) {
this.age = age;
}
}
运行结果:同一个线程添加的和取出的是同一个值
Thread-4xcy-1021967251
Thread-2xcy1234085798
Thread-1xcy207906111
Thread-3xcy1042697540
Thread-7xcy-420182191
Thread-0xcy127646899
Thread-6xcy-383177740
Thread-4xcy-1021967251
Thread-0xcy127646899
Thread-5xcy-80755785
Thread-5xcy-80755785
Thread-9xcy650715745
Thread-3xcy1042697540
Thread-8xcy1574576955
Thread-2xcy1234085798
Thread-1xcy207906111
Thread-9xcy650715745
Thread-9xcy650715745
Thread-5xcy-80755785
Thread-0xcy127646899
Thread-7xcy-420182191
Thread-6xcy-383177740
Thread-6xcy-383177740
Thread-7xcy-420182191
Thread-3xcy1042697540
Thread-4xcy-1021967251
Thread-2xcy1234085798
Thread-1xcy207906111
Thread-8xcy1574576955
Thread-8xcy1574576955
这个原理是什么呢?简单来讲就是创建了个HashMap,在每个线程中添加.put(当前thread名称,添加数据),然后取出的时候也是取出.get(当前thread名称),自然得到的是当前线程的数据。
上述例子升级版——单例模式
保证类有且只有一个实例,这里是保证每个线程有且只有一个student类,慢慢体会。struts中就是用的这个原理,每个线程的数据是隔离的
public class TestThreadlocal {
public static void main(String [] args)
{
for (int i=0;i<10;i++)
{
new Thread(new Runnable() {
public void run() {
Student student=Student.getInstance();
student.setAge(new Random().nextInt() +"");
student.setName("xcy");
System.out.println(Thread.currentThread().getName()+ student.getName()+student.getAge());
new A().get();
new B().get();
}
}){}.start();
}
}
static class A{
public void get (){
Student st=Student.getInstance();
System.out.println(Thread.currentThread().getName()+ st.getName()+st.getAge());
}
}
static class B{
public void get (){
Student st=Student.getInstance();
System.out.println(Thread.currentThread().getName()+ st.getName()+st.getAge());
}
}
}
student单例类
public class Student {
//////
Student(){}
private static ThreadLocal<Student> map=new ThreadLocal<Student>();
public static Student getInstance()
{
if (map.get()==null)
{
map.set(new Student());
}
return map.get();
}
//////
private String name;
private String age;
private boolean flag;
public boolean isFlag() {
return flag;
}
public void setFlag(boolean flag) {
this.flag = flag;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAge() {
return age;
}
public void setAge(String age) {
this.age = age;
}
}
三、数据共享
多线程之间就是因为数据共享在多个线程才导致了线程不安全,有些数据需要隔离,而有些必须要共享,比如火车票总数对买个买票的人共享。
这里主要有两种方式
1、传入Runnable赋值成员变量
共享数据封装成另外一个对象中封装成另外一个对象中,然后将这个对象逐一传递给各个Runnable对象,每个线程对共享数据的操作方法也分配到那个对象身上完成,这样容易实现针对数据进行各个操作的互斥和通信
2、新建外部类添加成员变量(这个是教程3中用到的,我比较常用)
与第一种方法的区别在于第二种方法巧妙的用了内部类共享外部类数据的思想,即把要共享的数据变得全局变量,这样就保证了操作的是同一份数据。同时内部类的方式使代码更加简洁。但是不如第一种解法条理那么清楚
四、总结
- 数据隔离threadlocal;
- 数据隔离单例模式;
- 数据共享data传入;
- 数据共享data存在外部类;