接收别人的项目别人的项目,发现了一段很夸张的代码,居然用源码的方式实现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 ResultData=new HashMap();
/**定时器**/
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 ResultData=new HashMap();
/**定时器**/
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也无效。
Timer的TaskQuery虽然取消,但是TaskQuery也不能执行到一半强制中断。所以继续执行时1. 正常的。
然后,new出来的TaskQuery,在一次使用后,不能再次放入Timer中,所以new是正常的。
然后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 ResultData=new HashMap();
/**定时器**/
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...结束执行...
好吧,可以接受。这算是手动的加了个锁。能解决问题就好。