背景
之前一個應用有一個小功能,每隔1分鐘創建一個線程來清理無效的http連接
主要邏輯之前是這樣寫的
private volatile boolean running = true;
@Override
public void run() {
try {
while (running) {
synchronized (this) {
wait(1000);
closeConnections();
}
}
} catch (InterruptedException ex) {
shutdown();
}
}
public void shutdown() {
running= false;
synchronized (this) {
notifyAll();
}
}
問題說明
最終的影響就是 不停地創建線程 直到線程溢出 OOM!
分析
有經驗的同學應該能看到這裏有個running,這個就是爲了標識線程要退出的信號量
但是 用錯了位置,或者說用錯了時間,導致線程一直是running狀態 而無法獲得notify退出
解決辦法
private volatile boolean running = true;
@Override
public void run() {
try {
while (running ) {
synchronized (this) {
wait(1000);
running= false;
connMgr.closeExpiredConnections();
connMgr.closeIdleConnections(5 * 60, TimeUnit.SECONDS);
logger.trace("clear closed http connections ok ,next to exit");
}
}
} catch (InterruptedException ex) {
shutdown();
}
}
public void shutdown() {
// running= false;need to mark at wait
synchronized (this) {
notifyAll();
}
}
效果
線程能正常被notify 然後退出
拓展
其實這樣寫也有不合理的地方 比如try while的範圍,一般來講 synchronized是寫在while外面的,而 try 是寫在 while裏面的,這樣也符合此場景下功能要求,當然這樣寫也不能說有啥大問題,總體是正能量的
另外 也不需要用wait, 因爲就這一個線程在處理,可以sleep再直接退出 不涉及競爭的問題。