接收別人的項目別人的項目,發現了一段很誇張的代碼,居然用源碼的方式實現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也無效。
- 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<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...結束執行...
好吧,可以接受。這算是手動的加了個鎖。能解決問題就好。