• 记一次线程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...结束执行...
    

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

  • 相关阅读:
    【移动端】300ms延迟以及点透事件原因以及解决方案
    javaScript drag对象进行拖拽使用详解
    js文件上传原理(form表单 ,FormData + XHR2 + FileReader + canvas)
    Linux常用bash命令
    一些好的关于网络知识的博客
    python 2 处理HTTP 请求的包
    python 3 处理HTTP 请求的包
    接口测试笔记
    接口测试资料
    PyH : python生成html
  • 原文地址:https://www.cnblogs.com/changfanchangle/p/8883279.html
Copyright © 2020-2023  润新知