(三) Java多线程详解之线程范围内共享变量及ThreadLocal类使用

来源:互联网 发布:海信网络电视看新闻 编辑:程序博客网 时间:2024/06/05 11:08

1.在开发中经常会遇到一种情况:有一个变量会被多个线程访问,但是要确保同个线程内,访问的是同一个对象,Hashtable方式实现代码如下:

public class ThreadExample5 {    private static Map<Thread, Integer> threadData = new Hashtable<Thread, Integer>();    public static void main(String[] args) {        for (int i = 0; i < 2; i++) {            new Thread(new Runnable() {                @Override                public void run() {                    int data = new Random().nextInt();                    System.out.println(Thread.currentThread().getName() + " has put data :" + data);                    threadData.put(Thread.currentThread(), data);                    new A().get();                    new B().get();                }            }).start();        }    }    static class A {        public void get() {            System.out.println(threadData);            int data = threadData.get(Thread.currentThread());            System.out.println("A from " + Thread.currentThread().getName() + " get data :" + data);        }    }    static class B {        public void get() {            int data = threadData.get(Thread.currentThread());            System.out.println("B from " + Thread.currentThread().getName() + " get data :" + data);        }    }}

注意这里用到的是Hashtable(线程安全)来区分每个线程,网上很多资料写的都是用HashMap(线程不安全),我经过实际测试发现此处用HashMap是有问题的。HashMap底层是一个Entry数组,当发生hash冲突的时候,HashMap是采用链表的方式来解决的,在对应的数组位置存放链表的头结点。对链表而言,新加入的节点会从头结点加入,假如A线程和B线程同时对同一个数组位置进行添加数据,两个线程会同时得到现在的头结点,然后A写入新的头结点之后,B也写入新的头结点,那B的写入操作就会覆盖A的写入操作造成A的写入操作丢失

2.ThreadLocal实现方式

public class ThreadExample6 {    private static ThreadLocal<Integer> x = new ThreadLocal<Integer>();    public static void main(String[] args) {        for (int i = 0; i < 2; i++) {            new Thread(new Runnable() {                @Override                public void run() {                    int data = new Random().nextInt();                    System.out.println(Thread.currentThread().getName() + " has put data :" + data);                    x.set(data);                    new A().get();                    new B().get();                }            }).start();        }    }    static class A {        public void get() {            int data = x.get();            System.out.println("A from " + Thread.currentThread().getName() + " get data :" + data);        }    }    static class B {        public void get() {            int data = x.get();            System.out.println("B from " + Thread.currentThread().getName() + " get data :" + data);        }    }}

以上存在的问题:一个ThreadLocal只能放一个数据,如果你有两个变量要线程范围内共享,则要定义两个ThreadLocal。如下为解决方案:

public class ThreadExample7 {    public static void main(String[] args) {        for (int i = 0; i < 2; i++) {            new Thread(new Runnable() {                @Override                public void run() {                    int data = new Random().nextInt();                    MyThreadScopeData.getThreadInstance().setName("name:名字" + data);                    MyThreadScopeData.getThreadInstance().setAge(data);                    new A().get();                    new B().get();                }            }).start();        }    }    static class A {        public void get() {            MyThreadScopeData myData = MyThreadScopeData.getThreadInstance();            System.out.println("A from " + Thread.currentThread().getName() + " getMyData: " + myData.getName() + "," + myData.getAge());        }    }    static class B {        public void get() {            MyThreadScopeData myData = MyThreadScopeData.getThreadInstance();            System.out.println("B from " + Thread.currentThread().getName() + " getMyData: " + myData.getName() + "," + myData.getAge());        }    }}class MyThreadScopeData {    private static ThreadLocal<MyThreadScopeData> map = new ThreadLocal<MyThreadScopeData>();    private MyThreadScopeData() {    }    public static synchronized MyThreadScopeData getThreadInstance() {        MyThreadScopeData instance = map.get();        if (instance == null) {            instance = new MyThreadScopeData();            map.set(instance);        }        return instance;    }    private String name;    private int age;    public String getName() {        return name;    }    public void setName(String name) {        this.name = name;    }    public int getAge() {        return age;    }    public void setAge(int age) {        this.age = age;    }}

此处解决的方式是用一个对象封装需要线程共享的对象,然后再把该对象绑定到ThreadLocal中, 实现线程范围内变量共享。此处对构造方法进行私有化,并且getThreadInstance()进行同步,目的是确保整个线程中只有一个MyThreadScopeData实例。

阅读全文
0 0
原创粉丝点击