您的位置:首页 > 编程语言 > Java开发

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

2017-05-20 11:03 721 查看

线程范围内共享变量

HashTable方式实现

在开发中经常会遇到一种情况:有一个变量会被多个线程访问,但是要确保同个线程内访问的是同一个对象,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的写入操作丢失

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实例
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息