Map在多线程中使用

来源:互联网 发布:淘宝上怎么搜dj香烟 编辑:程序博客网 时间:2024/05/18 13:07

背景:

在实际操作中经常会遇到一种场景,我们需要用一个成员属性Map来保存信息,这时我们可能起两个线程甚至更多,一个线程用来给Map装值,另外一个线程用来每隔一段时间就去Map那取值并清空Map。

实现:

根据上面场景需求,很简单,刷刷刷...,代码写好了,main thread用来给Map装东西,TimeTask  thread用来取Map里面的东西

public class AsyMap {private static Map<Integer, String> map = new HashMap<Integer, String>();public static void main(String[] args) throws InterruptedException {System.out.println(Thread.currentThread().getName());//启动时间操作类,每隔一段时间取值new Timer().schedule(new MyTask(), 500, 500);int i = 1;while(true){map.put(i, "content msg" + i);i++;Thread.sleep(200);}}static class MyTask extends TimerTask{@Overridepublic void run() {if (map.size() > 0) {Set<Entry<Integer, String>> entrySet = map.entrySet();for (Entry<Integer, String> entry : entrySet) {System.out.println(entry.getValue());}map.clear();}}}}
但是运行之后问题就来了。


原因是多线程的异步,但task在遍历Map取值是,Main thread 也还在往Map中装,这是Map不能容忍的,于是就罢工了。哦原来是异步引起的原因,好竟然HashMap异步不安全,那我用Hashtable,给它加锁总可以了吧!好一通改后代码成这样了

public class AsyTable {private static Map<Integer, String> table = new Hashtable<Integer, String>();public static void main(String[] args) throws InterruptedException {System.out.println(Thread.currentThread().getName());new Timer().schedule(new MyTask(), 500, 500);int i = 1;while(true){table.put(i, "content msg" + i);i++;Thread.sleep(200);}}static class MyTask extends TimerTask{@Overridepublic void run() {if (table.size() > 0) {Set<Entry<Integer, String>> entrySet = table.entrySet();for (Entry<Integer, String> entry : entrySet) {System.out.println(entry.getValue());}table.clear();}}}}

再重新运行,但不幸的是同样的问题再次爆出,这下不好玩了,再滤滤吧!翻翻API,原来Hashtable是给每个public方法加上同步锁当执行到table.entrySet()时它获得了锁,执行到foreach遍历时已经不是调用table的方法了,已经释放了锁,所以也是不行的。既然如此那我就锁对象,在每次从Map中获取值的时候就将它锁住,等到遍历完成后再讲锁释放。优化后代码如下:

public class AsyTable {private static Map<Integer, String> table = new Hashtable<Integer, String>();public static void main(String[] args) throws InterruptedException {System.out.println(Thread.currentThread().getName());new Timer().schedule(new MyTask(), 500, 500);int i = 1;while(true){table.put(i, "content msg" + i);i++;Thread.sleep(200);}}static class MyTask extends TimerTask{@Overridepublic void run() {if (table.size() > 0) {synchronized (table) {Set<Entry<Integer, String>> entrySet = table.entrySet();for (Entry<Integer, String> entry : entrySet) {System.out.println(entry.getValue());}table.clear();}}}}}

运行一下,project run perfestly.

总结:

项目中使用异步往往能提高项目运行效率,但有时候为了数据不被脏读取,则需要给对象加锁。


原创粉丝点击