假定现在有个公共的变量data,有不同的线程都可以去操作它,如果在不同的线程对data操作完成后再去取这个data,那末肯定会出现线程间的数据混乱问题,由于A线程在取data数据前可能B线程又对其进行了修改,下面写个程序来讲明1下该问题:
public class ThreadScopeShareData {
private static int data = 0;//公共的数据
public static void main(String[] args) {
for(int i = 0; i < 2; i ++) { //开启两个线程
new Thread(new Runnable() {
@Override
public void run() {
int temp = new Random().nextInt();
System.out.println(Thread.currentThread().getName() + " has put a data: " + temp); //打印出来为了看效果
data = temp; //操作数据:赋新值
new TestA().getData();
new TestB().getData();
}
}).start();
}
}
static class TestA {
public void getData() {
System.out.println("A get data from " + Thread.currentThread().getName() + ": " + data);//取出公共数据data
}
}
static class TestB {
public void getData() {
System.out.println("B get data from " + Thread.currentThread().getName() + ": " + data);
}
}
}
来看1下打印出来的结果:
Thread-0 has put a data: ⑴885917900
Thread⑴ has put a data: ⑴743455464
A get data from Thread-0: ⑴743455464
A get data from Thread⑴: ⑴743455464
B get data from Thread⑴: ⑴743455464
B get data from Thread-0: ⑴743455464
从结果中可以看出,两次对data赋的值确切不1样,但是两个线程最后打印出来的都是最后赋的那个值,说明Thread-0拿出的数据已不对了,这就是线程间同享数据带来的问题。
固然,我们完全可使用synchronized关键字将run()方法中的几行代码给套起来,这样每一个线程各自履行完,打印出各自的信息,这是没问题的,确切可以解决上面的线程间同享数据问题。但是,这是以其他线程被阻塞为代价的,即Thread-0在履行的时候,Thread⑴就被阻塞了,必须等待Thread-0履行完了才能履行。
那末如果我想两个线程同时跑,并且互不影响各自取出的值,该怎样办呢?这也是本文所要总结的重点,解决该问题的思想是:虽然现在都在操作公共数据data,但是不同的线程本身对这个data要保护1个副本,这个副本不是线程间所同享的,而是每一个线程所独有的,所以不同线程中所保护的data是不1样的,最后取的时候,是哪一个线程,我就从哪一个线程中取该data。
基于上面这个思路,我再把上面的程序做1修改,以下:
public class ThreadScopeShareData {
private static int data = 0;//公共的数据
//定义1个Map以键值对的方式存储每一个线程和它对应的数据,即Thread:data
private static Map<Thread, Integer> threadData = Collections.synchronizedMap(new HashMap<Thread, Integer>());
public static void main(String[] args) {
for(int i = 0; i < 2; i ++) {
new Thread(new Runnable() {
@Override
public void run() {
int temp = new Random().nextInt();
System.out.println(Thread.currentThread().getName() + " has put a data: " + temp); //打印出来为了看效果
threadData.put(Thread.currentThread(), temp); //向Map中存入本线程data数据的1个副本
data = temp; //操作数据:赋新值
new TestA().getData();
new TestB().getData();
}
}).start();
}
}
static class TestA {
public void getData() {
System.out.println("A get data from " + Thread.currentThread().getName() + ": "
+ threadData.get(Thread.currentThread())); //取出各线程保护的那个副本
}
}
static class TestB {
public void getData() {
System.out.println("B get data from " + Thread.currentThread().getName() + ": "
+ threadData.get(Thread.currentThread()));
}
}
}
上面程序中保护了1个Map,键值对分别是线程和它的数据,那末在操作data的时候,先把各自的数据保存到这个Map中,这样每一个线程保存的肯定不同,当再取的时候,根据当前线程对象作为key来取出对应的data副本,这样不同的线程之间就不会相互影响了。这个HashMap也需要包装1下,由于HashMap是非线程安全的,上面的程序中,不同的线程有对HashMap进行写操作,就有可能产生并提问题,所以也要包装1下。最后来看1下履行结果:
Thread-0 has put a data: 1817494992
Thread⑴ has put a data: ⑴189758355
A get data from Thread-0: 1817494992
B get data from Thread⑴: ⑴189758355
A get data from Thread-0: 1817494992
B get data from Thread⑴: ⑴189758355
就是线程范围内同享数据,即同1个线程里面这个数据是同享的,线程间是不同享的。
这让我联想到了学习数据库的时候用到的ThreadLocal,操作数据库需要connection,如果当前线程中有就拿当前线程中存的connection,否则就新建1个放到当前线程中,这样就不会出现问题,由于每一个线程本身同享了1个connection,它不是线程间同享的。这也很好理解,这个connection肯定不能同享,假定A和B用户都拿到这个connection并开启了事务,现在A开始转账了,但是钱还没转好,B转好了关闭了事务,那末A那边就出问题了。
线程范围内同享数据的问题就总结这么多吧~
相干浏览:http://blog.csdn.net/column/details/bingfa.html
—–乐于分享,共同进步!
—–更多文章请看:http://blog.csdn.net/eson_15