背景:
在实际操作中经常会遇到一种场景,我们需要用一个成员属性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.
总结:
项目中使用异步往往能提高项目运行效率,但有时候为了数据不被脏读取,则需要给对象加锁。