记一次线程Timer导致的线程安全问题修正

接收别人的项目别人的项目,发现了一段很夸张的代码,居然用源码的方式实现websocket……
还单独开了一个端口,多线程websocket,在调用Service的服务,定时执行什么的。
绕了好半天没有缓过劲来,不过自己debug的时候,没发现什么问题,就想着随它去吧。
结果过几天,报出了以下问题。

java.util.ConcurrentModificationException
	at java.util.HashMap$HashIterator.nextNode(HashMap.java:1437)
	at java.util.HashMap$EntryIterator.next(HashMap.java:1471)
	at java.util.HashMap$EntryIterator.next(HashMap.java:1469)
	.....

好吧,这是多线程访问HashMap导致的线程不安全问题。想改,但是单纯看实在看不出来。于是把原本代码,简化简化,做了个debug用项目出来。

package error_test_001;
import java.util.*;

public class error_test {
	public static void main(String[] arge) throws InterruptedException {
		Test ttmtransService = new Test();
		Runnable runnable = new Runnable() {
			@Override
			public void run() {
				try {
					Thread.sleep(1000);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				ttmtransService.StartTask();
			}
		};
        Thread thread[] = new Thread[100];
        for (int i = 0; i < thread.length; i++) {
            thread[i] = new Thread(runnable);
            thread[i].start();
        }
	}
}
class Test{
	/**返回结果数据**/
	private Map<String,Object> ResultData=new HashMap<String,Object>();
	/**定时器**/
	public static Timer TaskTimer;
	/**查询任务**/
	private TaskQuery mytask;

	/**开始执行任务**/
	public synchronized void StartTask(){
		if(TaskTimer != null){
			CloseTimer();
		}
		TaskTimer=new Timer();
		mytask=new TaskQuery();
		TaskTimer.schedule(mytask, new Date(),1000);
	}

	/**
	 * 关闭定时器
	 */
	private synchronized void CloseTimer(){
		TaskTimer.cancel();
		TaskTimer.purge();
		TaskTimer = null;
	}

	public class TaskQuery extends TimerTask{
		/** 原来时间戳 **/
		private long time;
		/** 现在时间戳 **/
		private long now;
		/** 执行中flg **/
		private boolean flgRunning = false;
		public void run(){
			if (flgRunning){
				return;
			}
			flgRunning = true;
			try {
				System.out.print("开始执行...");
				try {
					Thread.sleep(1000);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				System.out.println("结束执行...");
			}finally {
				// 结束时,flag
				flgRunning = false;
			}
		}
	}
}

上边的项目执行:

开始执行...开始执行...开始执行...结束执行...
结束执行...
结束执行...
开始执行...结束执行...
开始执行...结束执行...
开始执行...结束执行...
开始执行...结束执行...

好吧,至少确定有线程问题了。

其实,代码已经简化很多了,正常的一个Timer的话,是【task执行->task执行完毕->定时等待->task执行->task执行完毕->定时等待】,这样依次执行的,至少个人debug,在类似单线程环境下基本没为什么问题。
于是再仔细考虑了下代码,果然,问题出在Timer和TaskQuery的创建上呢,每次new对象,于是和之前的没关系,相互之间各自执行了。于是把定时器的开始和关闭方法StartTask和CloseTimer中的内容,改为全新建,全加的方法

package error_test_001;
import java.util.*;

public class error_test {
	public static void main(String[] arge) throws InterruptedException {
		Test ttmtransService = new Test();
		Runnable runnable = new Runnable() {
			@Override
			public void run() {
				try {
					Thread.sleep(1000);
					ttmtransService.StartTask();

					Thread.sleep(5000);
					ttmtransService.CloseTimer();
					ttmtransService.StartTask();

					Thread.sleep(5000);
					ttmtransService.CloseTimer();
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
		};
        Thread thread[] = new Thread[100];
        for (int i = 0; i < thread.length; i++) {
            thread[i] = new Thread(runnable);
            thread[i].start();
        }
	}
}
class Test{
	/**返回结果数据**/
	private Map<String,Object> ResultData=new HashMap<String,Object>();
	/**定时器**/
	public static Timer TaskTimer;
	/**查询任务**/
	private TaskQuery mytask;

	/**开始执行任务**/
	public synchronized void StartTask(){
		if(TaskTimer == null){
			System.out.print("StartTask...");
			TaskTimer=new Timer();
			mytask=new TaskQuery();
			TaskTimer.schedule(mytask, new Date(),1000);
		}
	}

	/**
	 * 关闭定时器
	 */
	public synchronized void CloseTimer(){
		if(TaskTimer != null){
			System.out.print("CloseTimer...");
			TaskTimer.cancel();
			TaskTimer.purge();
			TaskTimer = null;
		}
	}

	public class TaskQuery extends TimerTask{
		/** 原来时间戳 **/
		private long time;
		/** 现在时间戳 **/
		private long now;
		/** 执行中flg **/
		private boolean flgRunning = false;
		public void run(){
			if (flgRunning){
				return;
			}
			flgRunning = true;
			try {
				System.out.print("开始执行...");
				try {
					Thread.sleep(1000);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				System.out.println("结束执行...");
			}finally {
				// 结束时,flag
				flgRunning = false;
			}
		}
	}
}

然后看看结果:

StartTask...开始执行...结束执行...
开始执行...结束执行...
开始执行...结束执行...
开始执行...结束执行...
开始执行...结束执行...
CloseTimer...开始执行...StartTask...CloseTimer...StartTask...CloseTimer...开始执行...StartTask...CloseTimer...StartTask...CloseTimer...开始执行...StartTask...CloseTimer...StartTask...CloseTimer...StartTask...CloseTimer...StartTask...CloseTimer...开始执行...StartTask...CloseTimer...StartTask...CloseTimer...开始执行...StartTask...CloseTimer...StartTask...CloseTimer...StartTask...CloseTimer...StartTask...CloseTimer...StartTask...CloseTimer...StartTask...CloseTimer...StartTask...CloseTimer...开始执行...StartTask...CloseTimer...StartTask...CloseTimer...StartTask...CloseTimer...开始执行...StartTask...CloseTimer...StartTask...CloseTimer...StartTask...CloseTimer...开始执行...StartTask...CloseTimer...StartTask...CloseTimer...StartTask...CloseTimer...StartTask...CloseTimer...StartTask...CloseTimer...StartTask...CloseTimer...开始执行...StartTask...CloseTimer...StartTask...CloseTimer...StartTask...CloseTimer...StartTask...开始执行...CloseTimer...StartTask...CloseTimer...StartTask...CloseTimer...StartTask...CloseTimer...开始执行...StartTask...CloseTimer...StartTask...CloseTimer...StartTask...CloseTimer...开始执行...StartTask...CloseTimer...StartTask...CloseTimer...StartTask...CloseTimer...开始执行...StartTask...CloseTimer...开始执行...StartTask...CloseTimer...StartTask...CloseTimer...开始执行...StartTask...CloseTimer...StartTask...CloseTimer...开始执行...StartTask...CloseTimer...StartTask...CloseTimer...StartTask...CloseTimer...StartTask...CloseTimer...StartTask...CloseTimer...开始执行...StartTask...CloseTimer...StartTask...CloseTimer...StartTask...CloseTimer...StartTask...CloseTimer...开始执行...StartTask...CloseTimer...StartTask...CloseTimer...开始执行...StartTask...CloseTimer...StartTask...CloseTimer...StartTask...CloseTimer...StartTask...CloseTimer...StartTask...CloseTimer...StartTask...CloseTimer...StartTask...CloseTimer...StartTask...CloseTimer...StartTask...CloseTimer...StartTask...CloseTimer...StartTask...CloseTimer...StartTask...CloseTimer...StartTask...CloseTimer...StartTask...CloseTimer...StartTask...CloseTimer...StartTask...CloseTimer...StartTask...CloseTimer...StartTask...CloseTimer...StartTask...CloseTimer...StartTask...CloseTimer...StartTask...CloseTimer...StartTask...CloseTimer...StartTask...CloseTimer...StartTask...CloseTimer...StartTask...CloseTimer...StartTask...CloseTimer...StartTask...CloseTimer...StartTask...CloseTimer...StartTask...CloseTimer...StartTask...CloseTimer...StartTask...CloseTimer...StartTask...CloseTimer...StartTask...CloseTimer...开始执行...StartTask...CloseTimer...StartTask...CloseTimer...StartTask...CloseTimer...StartTask...CloseTimer...StartTask...CloseTimer...StartTask...开始执行...结束执行...
结束执行...
结束执行...
结束执行...
结束执行...
结束执行...
结束执行...
结束执行...
结束执行...
结束执行...
结束执行...
结束执行...
结束执行...
结束执行...
结束执行...
结束执行...
结束执行...
结束执行...
结束执行...
结束执行...
结束执行...
开始执行...结束执行...
开始执行...结束执行...
开始执行...结束执行...
开始执行...CloseTimer...结束执行...

嗯……开始是没错,但是中间尝试多线程停止timer并重新开始的时候出问题了。反复的开始关闭交错执行,每次都会新开区一个TaskQuery,导致有很多tark新执行。而这些TaskQuery都是new出来的,于是使用synchronized也无效。

  1. Timer的TaskQuery虽然取消,但是TaskQuery也不能执行到一半强制中断。所以继续执行时1. 正常的。
  2. 然后,new出来的TaskQuery,在一次使用后,不能再次放入Timer中,所以new是正常的。
  3. 然后Timer没发判断内部的执行状态,没想做延时也不行。

好吧,然后又是看代码……说起来,里边有个flgRunning呢,定义在内部是没什么用的,那么拿出来试试?

package error_test_001;
import java.util.*;

public class error_test {
	public static void main(String[] arge) throws InterruptedException {
		Test ttmtransService = new Test();
		Runnable runnable = new Runnable() {
			@Override
			public void run() {
				try {
					Thread.sleep(1000);
					ttmtransService.StartTask();

					Thread.sleep(5000);
					ttmtransService.CloseTimer();
					ttmtransService.StartTask();

					Thread.sleep(10000);
					ttmtransService.CloseTimer();
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
		};
        Thread thread[] = new Thread[100];
        for (int i = 0; i < thread.length; i++) {
            thread[i] = new Thread(runnable);
            thread[i].start();
        }
	}
}
class Test{
	/**返回结果数据**/
	private Map<String,Object> ResultData=new HashMap<String,Object>();
	/**定时器**/
	public static Timer TaskTimer;
	/**查询任务**/
	private TaskQuery mytask;
	/** 执行中flg **/
	private boolean flgRunning = false;

	/**开始执行任务**/
	public synchronized void StartTask(){
		if(TaskTimer == null){
			System.out.print("StartTask...");
			TaskTimer=new Timer();
			mytask=new TaskQuery();
			TaskTimer.schedule(mytask, new Date(),1000);
		}
	}

	/**
	 * 关闭定时器
	 */
	public synchronized void CloseTimer(){
		if(TaskTimer != null){
			System.out.print("CloseTimer...");
			TaskTimer.cancel();
			TaskTimer.purge();
			TaskTimer = null;
		}
	}

	public class TaskQuery extends TimerTask{
		/** 原来时间戳 **/
		private long time;
		/** 现在时间戳 **/
		private long now;
		public void run(){
			if (flgRunning){
				return;
			}
			flgRunning = true;
			try {
				System.out.print("开始执行...");
				try {
					Thread.sleep(1000);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				System.out.println("结束执行...");
			}finally {
				// 结束时,flag
				flgRunning = false;
			}
		}
	}
}

结果:

StartTask...开始执行...结束执行...
开始执行...结束执行...
开始执行...结束执行...
开始执行...结束执行...
开始执行...CloseTimer...StartTask...CloseTimer...StartTask...CloseTimer...StartTask...CloseTimer...StartTask...结束执行...
CloseTimer...StartTask...CloseTimer...StartTask...开始执行...CloseTimer...StartTask...CloseTimer...StartTask...CloseTimer...StartTask...CloseTimer...StartTask...CloseTimer...StartTask...CloseTimer...StartTask...CloseTimer...StartTask...CloseTimer...StartTask...CloseTimer...StartTask...CloseTimer...StartTask...CloseTimer...StartTask...CloseTimer...StartTask...CloseTimer...StartTask...CloseTimer...StartTask...CloseTimer...StartTask...CloseTimer...StartTask...CloseTimer...StartTask...CloseTimer...StartTask...CloseTimer...StartTask...CloseTimer...StartTask...CloseTimer...StartTask...CloseTimer...StartTask...CloseTimer...StartTask...CloseTimer...StartTask...CloseTimer...StartTask...CloseTimer...StartTask...CloseTimer...StartTask...CloseTimer...StartTask...CloseTimer...StartTask...CloseTimer...StartTask...CloseTimer...StartTask...CloseTimer...StartTask...CloseTimer...StartTask...CloseTimer...StartTask...CloseTimer...StartTask...CloseTimer...StartTask...CloseTimer...StartTask...CloseTimer...StartTask...CloseTimer...StartTask...CloseTimer...StartTask...CloseTimer...StartTask...CloseTimer...StartTask...CloseTimer...StartTask...CloseTimer...StartTask...CloseTimer...StartTask...CloseTimer...StartTask...CloseTimer...StartTask...CloseTimer...StartTask...CloseTimer...StartTask...CloseTimer...StartTask...CloseTimer...StartTask...CloseTimer...StartTask...CloseTimer...StartTask...CloseTimer...StartTask...CloseTimer...StartTask...CloseTimer...StartTask...CloseTimer...StartTask...CloseTimer...StartTask...CloseTimer...StartTask...CloseTimer...StartTask...CloseTimer...StartTask...CloseTimer...StartTask...CloseTimer...StartTask...CloseTimer...StartTask...CloseTimer...StartTask...CloseTimer...StartTask...CloseTimer...StartTask...CloseTimer...StartTask...CloseTimer...StartTask...CloseTimer...StartTask...CloseTimer...StartTask...CloseTimer...StartTask...CloseTimer...StartTask...CloseTimer...StartTask...CloseTimer...StartTask...CloseTimer...StartTask...CloseTimer...StartTask...CloseTimer...StartTask...CloseTimer...StartTask...CloseTimer...StartTask...CloseTimer...StartTask...CloseTimer...StartTask...CloseTimer...StartTask...CloseTimer...StartTask...CloseTimer...StartTask...CloseTimer...StartTask...CloseTimer...StartTask...CloseTimer...StartTask...CloseTimer...StartTask...CloseTimer...StartTask...CloseTimer...StartTask...CloseTimer...StartTask...结束执行...
开始执行...结束执行...
开始执行...结束执行...
开始执行...结束执行...
开始执行...结束执行...
开始执行...结束执行...
开始执行...结束执行...
开始执行...结束执行...
开始执行...结束执行...
开始执行...CloseTimer...结束执行...

好吧,可以接受。这算是手动的加了个锁。能解决问题就好。

posted @ 2018-04-19 15:29  常烦常乐  阅读(1981)  评论(0编辑  收藏  举报