Map在多线程中使用

xiaoxiao2021-02-28  83

背景:

在实际操作中经常会遇到一种场景,我们需要用一个成员属性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{ @Override public 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{ @Override public 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{ @Override public 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.

总结:

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

转载请注明原文地址: https://www.6miu.com/read-75030.html

最新回复(0)