Map在多线程中使用

背景:

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

总结:

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


评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值