Java多线程/并发13、保持线程间的数据独立: Collections.synchronizedMap应用
来源:互联网 发布:php mvc框架有哪些 编辑:程序博客网 时间:2024/05/22 10:34
现在流行分布式计算,分布式计算就是先分开计算,然后统一汇总。比如这道题目: 。先别跑,小学题很简单的。
解释一下,左边那一砣是计算从1加到n的值(求和),右边是n乘到1的值(阶乘),再把两个值相加得到最终结果。假设求和运算需要5秒钟,阶乘运算需要7秒钟,相加的运算需要1秒,那么总耗时是13秒。而在分布式计算中,由两台机器同时进行计算,得到求和及阶乘的两个结果只需要7秒,再相加需要1秒,总耗时8秒。
下面,我们通过两个线程来模拟这个过程。
首先定义计算类,这三个类作为外部类,存在于Main()函数所在类的外面。代码很简单,看起来很长,是因为加了几组try..catch对。
/* 定义计算器,让计算由统一的类来管理 */class Calculator { public static int result = 0; void getFactorial(int n) throws Exception { /* 模拟阶乘运算需要7秒 */ try { Thread.sleep(7000); } catch (InterruptedException e) { e.printStackTrace(); } try { /* 进行阶乘运算 */ result = Factorial.GetResult(n); } catch (Exception e) { throw new Exception(e.getMessage()); } System.out.println("线程" + Thread.currentThread().getName() + "输出结果:" + String.valueOf(Calculator.result)); } void getAccu(int n) throws Exception { /* 模拟求和运算需要5秒 */ try { Thread.sleep(5000); } catch (InterruptedException e) { e.printStackTrace(); } try { result = Accu.GetResult(n);/* 进行求和运算 */ } catch (Exception e) { throw new Exception(e.getMessage()); } System.out.println("线程" + Thread.currentThread().getName() + "输出结果:" + String.valueOf(Calculator.result)); }}/* 定义阶乘处理类Factorial 这里是模拟实现,实际开发中需要用大数处理类BigInteger才行*/class Factorial { public static int GetResult(int n) throws Exception { if (n < 0 || n > 10) { throw new Exception("error:玩玩而已,输入不能小于零,也不能大于10"); } if (n <= 2) { return n; } return n * GetResult(n - 1); }}/* 定义求和处理类Accu */class Accu { public static int GetResult(int n) throws Exception { if (n < 0 || n >= 1000) { throw new Exception("error:玩玩而已,输入不能小于零,也不能大于1000"); } if (n <= 1) { return n; } return n + GetResult(n - 1); }}
接下来,在Main()主线程中开始调用类来计算。
public class ExecuteDemo { public static void main(String[] args) { final Calculator calculator = new Calculator(); new Thread(new Runnable() { public void run() { try { calculator.getFactorial(10); } catch (Exception e) { System.err.println(e.getMessage()); } /*模拟很多线程并发运行时造成的竞争*/ try { Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("Factorial结果:" + String.valueOf(Calculator.result)); } }).start(); new Thread(new Runnable() { public void run() { try { calculator.getAccu(10); } catch (Exception e) { System.err.println(e.getMessage()); } /*模拟很多线程并发运行时造成的竞争*/ try { Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("Accu结果:" + String.valueOf(Calculator.result)); } }).start(); }}
输出结果:
线程Thread-1输出结果:55线程Thread-0输出结果:3628800Accu结果:3628800Factorial结果:3628800
线程调用方法运算的值是正确的,但是在取值却存在错误,Thread-1的计算结果被覆盖了。看来写出的这个计算类并不是线程安全的,问题就出在存放计算结果的变量static int result上。因为静态成员(static member)作为公共变量,就是放在共享内存区域的(方法区)。
在Calculator类中,我们用一个static int result来保存类中成员方法计算的结果,但多个线程并发调用方法时,都会抢占result向其写入数据,最终只会保留最后一个线程计算的值。我们需要让每个线程都保持各自的计算结果,自然想到了HashMap来保存。java提供了一个线程安全的HashMap包装: Collections.synchronizedMap。
我们来改动一下Calculator类,代码如下:
class Calculator { public static Map<Thread, Integer> DataContainer = Collections.synchronizedMap(new HashMap<Thread, Integer>()); void getFactorial(int n) throws Exception { /* 模拟阶乘运算需要7秒 */ try { Thread.sleep(7000); } catch (InterruptedException e) { e.printStackTrace(); } int result=0; try { result = Factorial.GetResult(n);/* 进行阶乘运算 */ DataContainer.put(Thread.currentThread(), result); } catch (Exception e) { throw new Exception(e.getMessage()); } System.out.println("线程" + Thread.currentThread().getName() + "输出结果:" + String.valueOf(result)); } void getAccu(int n) throws Exception { /* 模拟求和运算需要5秒 */ try { Thread.sleep(5000); } catch (InterruptedException e) { e.printStackTrace(); } int result=0; try { result = Accu.GetResult(n);/* 进行求和运算 */ DataContainer.put(Thread.currentThread(), result); } catch (Exception e) { throw new Exception(e.getMessage()); } System.out.println("线程" + Thread.currentThread().getName() + "输出结果:" + String.valueOf(result)); }}
Main()函数的输出调整:
public static void main(String[] args) { final Calculator calculator = new Calculator(); new Thread(new Runnable() { public void run() { try { calculator.getFactorial(10); } catch (Exception e) { System.err.println(e.getMessage()); } /* 模拟耗时操作中多个线程运行时抢占造成的竞争 */ try { Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("Factorial结果:" + String.valueOf(Calculator.DataContainer.get(Thread .currentThread()))); } }).start(); new Thread(new Runnable() { public void run() { try { calculator.getAccu(10); } catch (Exception e) { System.err.println(e.getMessage()); } /* 模拟耗时操作中多个线程运行时抢占造成的竞 */ try { Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("Accu结果:" + String.valueOf(Calculator.DataContainer.get(Thread .currentThread()))); } }).start(); }
两个线程互不影响,成功输出:
线程Thread-1输出结果:55线程Thread-0输出结果:3628800Accu结果:55Factorial结果:3628800
最后,计算两个结果的和
在Main()函数两个线程调用的后面,加上下面这段,看看输出的结果:
Iterator<Map.Entry<Thread, Integer>> it=Calculator.DataContainer.entrySet().iterator();int sum=0;while(it.hasNext()){ Map.Entry<Thread, Integer> entry = (Map.Entry<Thread, Integer>) it.next(); Integer value = (Integer)entry.getValue(); sum+=value;}System.out.println("最终计算结果"+String.valueOf(sum));
运行输出:
最终计算结果0线程Thread-1输出结果:55线程Thread-0输出结果:3628800Accu结果:55Factorial结果:3628800
我K,还是不对。
原来,进入Main()函数后,开启两个子线程计算后,主线程并没有停止,继续往下运行。由于两个子线程是耗时操作,而主线程如小李飞刀般快的速度往下运行,这时侯Calculator.DataContainer还是空的,结果当然为0。
我们应该阻塞主线程,直到Calculator.DataContainer有两个计算结果值后,才允许计算最后的相加结果。
在Iterator it=Calculator.DataContainer.entrySet().iterator();
这句的前面加上:
while(Calculator.DataContainer.size()<2){ try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } }
还有一种方法,调用两个线程的join()方法来实现调用线程(主线程)阻塞,等两个线程运行完毕再继续执行主线程,不过这要求两个线程不能用匿名方式实现,这里就不改程序了。具体join用法参考《Thread.Join()让调用线程等待子线程 》
运行输出:
线程Thread-1输出结果:55线程Thread-0输出结果:3628800最终计算结果3628855Accu结果:55Factorial结果:3628800
- Java多线程/并发13、保持线程间的数据独立: Collections.synchronizedMap应用
- Java多线程/并发14、保持线程间的数据独立:ConcurrentHashMap应用
- Java多线程/并发15、保持线程间的数据独立:ThreadLocal应用
- java多线程研究(六)保持各线程间的变量独立
- Hashtable, HashMap, Collections.synchronizedMap, ConcurrentHashMap 多线程并发特行分析
- java.util.Collections;Collections .synchronizedMap
- Java多线程与并发应用-(6)-多个线程之间共享对象和数据的方式
- java多线程并发库高级应用 之 多个线程之间共享数据的方式探讨
- Java多线程/并发05、synchronized应用实例:线程间操作共享数据
- Java Collections.synchronizedMap方法分析
- Java Collections.synchronizedMap方法分析
- 【Java多线程】并发容器Collections
- 【Java多线程与并发库】8.java5线程并发库之线程池的应用
- 【Java多线程与并发库】8.java5线程并发库之线程池的应用
- Collections.synchronizedMap
- Collections.synchronizedMap()
- Java多线程与并发库高级应用之线程数据交换Exchanger
- java多线程并发库高级应用 之 线程范围内共享数据
- XiyouLinux兴趣小组免试题——level 5
- JS -ECMAScript
- Spring MVC 入门
- 添加事物管理
- 重复验证和验证码
- Java多线程/并发13、保持线程间的数据独立: Collections.synchronizedMap应用
- tensorflow之线性回归
- 深度学习:前馈神经网络neural network
- 使用canvas实现图片压缩上传
- mapreduce计算分词权重
- mysql
- 如何最快速的找到页面某一元素所绑定的点击事件,并查看js代码
- J
- PHP curl实现get/post/delete/put封装