John.Ying-应志浩
2 years ago
3 changed files with 288 additions and 5 deletions
@ -0,0 +1,73 @@ |
|||||||
|
package com.fr.design.carton.developer; |
||||||
|
|
||||||
|
import java.awt.AWTEvent; |
||||||
|
|
||||||
|
/** |
||||||
|
* EDT事件的包装类,用来额外处理信息 |
||||||
|
* |
||||||
|
* @author John.Ying |
||||||
|
* @since 11.0 |
||||||
|
* Created on 2023/4/14 |
||||||
|
*/ |
||||||
|
public class AwtEventInfo { |
||||||
|
//获取执行该事件的线程
|
||||||
|
private final Thread eventDispatchThread = Thread.currentThread(); |
||||||
|
//在队列中等待执行的事件最后未执行的时间,当有一个事件执行完后就遍历dispatches给该值赋当前时间
|
||||||
|
private long lastDispatchTimeMillis = System.currentTimeMillis(); |
||||||
|
//事件开始的时间
|
||||||
|
private final long startDispatchTimeMillis = System.currentTimeMillis(); |
||||||
|
//awt事件
|
||||||
|
private AWTEvent awtEvent; |
||||||
|
//事件堆栈
|
||||||
|
private StackTraceElement[] stackTrace; |
||||||
|
|
||||||
|
/** |
||||||
|
* 检查是否要给堆栈赋值 |
||||||
|
*/ |
||||||
|
public void checkForHang() { |
||||||
|
if (isNeedToStackTrace()) { |
||||||
|
this.stackTrace = eventDispatchThread.getStackTrace(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 是否需要赋值堆栈满足 |
||||||
|
* 耗时>20ms |
||||||
|
*/ |
||||||
|
private boolean isNeedToStackTrace() { |
||||||
|
return (System.currentTimeMillis() - startDispatchTimeMillis) > EventDispatchDeveloperMode.MAX_TIME |
||||||
|
&& stackTrace == null; |
||||||
|
} |
||||||
|
|
||||||
|
public Thread getEventDispatchThread() { |
||||||
|
return eventDispatchThread; |
||||||
|
} |
||||||
|
|
||||||
|
public long getLastDispatchTimeMillis() { |
||||||
|
return lastDispatchTimeMillis; |
||||||
|
} |
||||||
|
|
||||||
|
public void setLastDispatchTimeMillis(long lastDispatchTimeMillis) { |
||||||
|
this.lastDispatchTimeMillis = lastDispatchTimeMillis; |
||||||
|
} |
||||||
|
|
||||||
|
public long getStartDispatchTimeMillis() { |
||||||
|
return startDispatchTimeMillis; |
||||||
|
} |
||||||
|
|
||||||
|
public AWTEvent getAwtEvent() { |
||||||
|
return awtEvent; |
||||||
|
} |
||||||
|
|
||||||
|
public void setAwtEvent(AWTEvent awtEvent) { |
||||||
|
this.awtEvent = awtEvent; |
||||||
|
} |
||||||
|
|
||||||
|
public StackTraceElement[] getStackTrace() { |
||||||
|
return stackTrace; |
||||||
|
} |
||||||
|
|
||||||
|
public void setStackTrace(StackTraceElement[] stackTrace) { |
||||||
|
this.stackTrace = stackTrace; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,146 @@ |
|||||||
|
package com.fr.design.carton.developer; |
||||||
|
|
||||||
|
|
||||||
|
import com.fr.concurrent.FineExecutors; |
||||||
|
import com.fr.design.carton.EventDispatchThreadHangMonitor; |
||||||
|
import com.fr.design.ui.util.UIUtil; |
||||||
|
import com.fr.log.FineLoggerFactory; |
||||||
|
|
||||||
|
import java.awt.AWTEvent; |
||||||
|
import java.awt.EventQueue; |
||||||
|
import java.awt.Toolkit; |
||||||
|
import java.util.LinkedList; |
||||||
|
import java.util.Timer; |
||||||
|
import java.util.TimerTask; |
||||||
|
import java.util.concurrent.ScheduledExecutorService; |
||||||
|
import java.util.concurrent.TimeUnit; |
||||||
|
|
||||||
|
/** |
||||||
|
* 开发者模式重写的EDT |
||||||
|
* |
||||||
|
* @author John.Ying |
||||||
|
* @since 11.0 |
||||||
|
* Created on 2023/4/14 |
||||||
|
*/ |
||||||
|
public final class EventDispatchDeveloperMode extends EventQueue { |
||||||
|
|
||||||
|
/** |
||||||
|
* 该链表为主要的实现定时任务的容器,在重写的dispatchEvent中由pre方法将DispatchInfo加入到链表,由post方法remove |
||||||
|
*/ |
||||||
|
private final LinkedList<AwtEventInfo> dispatches = new LinkedList<>(); |
||||||
|
/** |
||||||
|
* 开启间隔检测后两次检测的相隔时间ms |
||||||
|
*/ |
||||||
|
public static final long CHECK_INTERVAL_MS = 5; |
||||||
|
|
||||||
|
private Timer timer; |
||||||
|
|
||||||
|
/** |
||||||
|
* edt事件最大允许的时间 |
||||||
|
*/ |
||||||
|
public static final long MAX_TIME = 20; |
||||||
|
public static final EventDispatchDeveloperMode INSTANCE = new EventDispatchDeveloperMode(); |
||||||
|
|
||||||
|
@Override |
||||||
|
protected void dispatchEvent(AWTEvent event) { |
||||||
|
try { |
||||||
|
preDispatchEvent(event); |
||||||
|
super.dispatchEvent(event); |
||||||
|
} finally { |
||||||
|
postDispatchEvent(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 事件分发前处理 |
||||||
|
*/ |
||||||
|
private synchronized void preDispatchEvent(AWTEvent event) { |
||||||
|
synchronized (dispatches) { |
||||||
|
AwtEventInfo awtEventInfo = new AwtEventInfo(); |
||||||
|
awtEventInfo.setAwtEvent(event); |
||||||
|
dispatches.addLast(awtEventInfo); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 事件分发后处理 |
||||||
|
*/ |
||||||
|
private synchronized void postDispatchEvent() { |
||||||
|
synchronized (dispatches) { |
||||||
|
AwtEventInfo awtEventInfo = dispatches.removeLast(); |
||||||
|
//嵌套最深的事件执行完毕后刷新链表中其他事件的lastDispatchTimeMillis
|
||||||
|
Thread currentEventDispatchThread = Thread.currentThread(); |
||||||
|
for (AwtEventInfo info : dispatches) { |
||||||
|
info.setLastDispatchTimeMillis(System.currentTimeMillis()); |
||||||
|
} |
||||||
|
long nowTime = System.currentTimeMillis(); |
||||||
|
long totalTime = nowTime - awtEventInfo.getStartDispatchTimeMillis(); |
||||||
|
long continuationTime = nowTime - awtEventInfo.getLastDispatchTimeMillis(); |
||||||
|
if (continuationTime > MAX_TIME) { |
||||||
|
FineLoggerFactory.getLogger() |
||||||
|
.warn("awt event spend time more than 20ms, totalTime {} continuationTime {} the stack is {}" |
||||||
|
, totalTime, continuationTime, EventDispatchThreadHangMonitor.stackTraceToStringForConsole(awtEventInfo.getStackTrace())); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 将swing中默认的EventQueue换成自己的 |
||||||
|
*/ |
||||||
|
public void initMonitoring() { |
||||||
|
UIUtil.invokeLaterIfNeeded(() -> Toolkit.getDefaultToolkit().getSystemEventQueue().push(INSTANCE)); |
||||||
|
initTimer(); |
||||||
|
startFilterModalWindow(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Sets up a timer to check for hangs frequently. |
||||||
|
* 初始化一个Timer |
||||||
|
*/ |
||||||
|
public void initTimer() { |
||||||
|
final long initialDelayMs = 0; |
||||||
|
final boolean daemon = true; |
||||||
|
timer = new Timer("EventDispatchDeveloperMode", daemon); |
||||||
|
timer.schedule(new HangChecker(), initialDelayMs, CHECK_INTERVAL_MS); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* /消除Timer |
||||||
|
*/ |
||||||
|
public void stopTimer() { |
||||||
|
if (timer != null) { |
||||||
|
timer.cancel(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* /定时执行的任务 |
||||||
|
*/ |
||||||
|
public class HangChecker extends TimerTask { |
||||||
|
@Override |
||||||
|
public void run() { |
||||||
|
synchronized (dispatches) { |
||||||
|
//如果链表为空定时检测就不进行
|
||||||
|
if (dispatches.isEmpty()) { |
||||||
|
return; |
||||||
|
} |
||||||
|
dispatches.getLast().checkForHang(); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 消除模态框影响 |
||||||
|
*/ |
||||||
|
public void startFilterModalWindow() { |
||||||
|
ScheduledExecutorService scheduledExecutorService = FineExecutors.newSingleThreadScheduledExecutor(); |
||||||
|
scheduledExecutorService.scheduleAtFixedRate(new Runnable() { |
||||||
|
@Override |
||||||
|
public void run() { |
||||||
|
UIUtil.invokeLaterIfNeeded(() -> { |
||||||
|
//不用干事,切个片就可以
|
||||||
|
}); |
||||||
|
} |
||||||
|
}, 0, 10, TimeUnit.MILLISECONDS); |
||||||
|
} |
||||||
|
} |
Loading…
Reference in new issue