博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Java并发基础06. 线程范围内共享数据
阅读量:6828 次
发布时间:2019-06-26

本文共 4001 字,大约阅读时间需要 13 分钟。

假设现在有个公共的变量 data,有不同的线程都可以去操作它,如果在不同的线程对 data 操作完成后再去取这个 data,那么肯定会出现线程间的数据混乱问题,因为 A 线程在取 data 数据前可能 B 线程又对其进行了修改,下面写个程序来说明一下该问题:

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);        }    }}

来看一下打印出来的结果:

Thread-0 has put a data: -1885917900

Thread-1 has put a data: -1743455464
A get data from Thread-0: -1743455464
A get data from Thread-1: -1743455464
B get data from Thread-1: -1743455464
B get data from Thread-0: -1743455464

从结果中可以看出,两次对 data 赋的值确实不一样,但是两个线程最后打印出来的都是最后赋的那个值,说明 Thread-0 拿出的数据已经不对了,这就是线程间共享数据带来的问题。

当然,我们完全可以使用 synchronized 关键字将 run() 方法中的几行代码给套起来,这样每个线程各自执行完,打印出各自的信息,这是没问题的,确实可以解决上面的线程间共享数据问题。但是,这是以其他线程被阻塞为代价的,即 Thread-0 在执行的时候,Thread-1 就被阻塞了,必须等待 Thread-0 执行完了才能执行。

那么如果我想两个线程同时跑,并且互不影响各自取出的值,该怎么办呢?这也是本文所要总结的重点,解决该问题的思想是:虽然现在都在操作公共数据 data,但是不同的线程本身对这个 data 要维护一个副本,这个副本不是线程间所共享的,而是每个线程所独有的,所以不同线程中所维护的 data 是不一样的,最后取的时候,是哪个线程,我就从哪个线程中取该 data。

基于上面这个思路,我再把上面的程序做一修改,如下:

public class ThreadScopeShareData {    private static int data = 0;//公共的数据    //定义一个Map以键值对的方式存储每个线程和它对应的数据,即Thread:data    private static Map
threadData = Collections.synchronizedMap(new HashMap
()); 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数据的一个副本 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())); } }}

上面程序中维护了一个 Map,键值对分别是线程和它的数据,那么在操作 data 的时候,先把各自的数据保存到这个 Map 中,这样每个线程保存的肯定不同,当再取的时候,根据当前线程对象作为 key 来取出对应的 data 副本,这样不同的线程之间就不会相互影响了。这个 HashMap 也需要包装一下,因为 HashMap 是非线程安全的,上面的程序中,不同的线程有对 HashMap 进行写操作,就有可能产生并发问题,所以也要包装一下。最后来看一下执行结果:

Thread-0 has put a data: 1817494992

Thread-1 has put a data: -1189758355
A get data from Thread-0: 1817494992
A get data from Thread-1: -1189758355
B get data from Thread-0: 1817494992
B get data from Thread-1: -1189758355

就是线程范围内共享数据,即同一个线程里面这个数据是共享的,线程间是不共享的。

这让我联想到了学习数据库的时候用到的 ThreadLocal,操作数据库需要 connection,如果当前线程中有就拿当前线程中存的 connection,否则就新建一个放到当前线程中,这样就不会出现问题,因为每个线程本身共享了一个 connection,它不是线程间共享的。这也很好理解,这个 connection 肯定不能共享,假设 A 和 B 用户都拿到这个 connection 并开启了事务,现在 A 开始转账了,但是钱还没转好,B 转好了关闭了事务,那么A那边就出问题了。

线程范围内共享数据的问题就总结这么多吧~如果有问题,欢迎指正,我们一起进步!

转载于:https://www.cnblogs.com/eson15/p/10235528.html

你可能感兴趣的文章
PyTalk : a Jabber Client un Python using xmpppy and PyQt4
查看>>
利用ResultFilter实现asp.net mvc 页面静态化
查看>>
C++类构造函数初始化列表(转)
查看>>
13最佳WordPress的维护插件
查看>>
Missing Screenshot 的解决方案
查看>>
jQuery:1.5.1,复选框应用(全选,全不选,反选,提交选中的值,全选/全不选)...
查看>>
jQuery:1.5.4.3,表格变色(单击行,把当行的单选按钮(radio)设为选中状态,并应用当前样式)...
查看>>
oracle11gR2安装示例数据库
查看>>
解决ssh的"Write failed: Broken pipe"问题
查看>>
Java 网络编程(五) 使用TCP/IP的套接字(Socket)进行通信
查看>>
chromium如何新增extension API以及添加内部扩展
查看>>
拒绝alert调试js,浏览器调试js大全(火狐firefox浏览器,谷歌chrome 浏览器,微软ie9浏览器等)...
查看>>
EJDB 1.1.18 发布,嵌入式JSON数据库
查看>>
session和cookie的联系
查看>>
Go Revel - Templates(模板)
查看>>
《深入理解Nginx》阅读与实践(三):使用upstream和subrequest访问第三方服务
查看>>
NGUI:HUD Text(头顶伤害漂浮文字)
查看>>
HTML/CSS/Javascript代码在线压缩、格式化(美化)工具
查看>>
linux命令学习-复制(cp,scp)
查看>>
cocos2d-x开发记录:二,基本概念(粒子系统,Scheduler和定时器)
查看>>