java 中断代码_你的java代码可中断吗?(2)
1.確保提交到線程池的任務可中斷
原文:www.securecoding.cert.org,TPS02-J. Ensure thattasks submitted to a thread pool are interruptible。
為了能完全關閉線程池或者取消線程池中的個別任務,程序應提交支持使用Thread.interrupt()中斷的任務到線程池。程序不應提交不支持中斷的任務到線程池,除非是無阻塞且短時間運行的任務。
根據java.util.concurrent.ExecutorService.shutdownNow()?Java API的描述:
嘗試終止所有正在執行的任務,停止所有等待處理的任務,并且返回等待執行的任務列表…
除了盡最大努力嘗試終止正在執行的任務之外,沒有任何其他的保證。例如,典型實現是通過Thread.interrupt()進行取消,因此任何不能中斷的任務可能永遠不會被終止。
違規代碼示例(Shutting Down Thread Pools)
如下是違規代碼示例,提交SocketReader任務到PoolService聲明的線程池中:
因為任務不支持Thread.interrupt()進行中斷,且因為shutdown()方法必須等到所有正執行的任務執行完成,所以shutdownNow()方法可能不能立即完全關閉線程池。
同樣,某些不用Thread.interrupted() 而使用其他的機制來決定何時關閉的任務,其是無法響應shutdown()和shutdownNow()。例如,通過檢查一個volatile標志位來決定是否可安全關閉的任務,對這些方法是無法響應的。THI05-J. Do not use Thread.stop() to terminate threads提供了使用標志位終止線程的更多信息。
合規解決方案(Submit InterruptibleTasks)
如下合規解決方案定義了SocketReader的一個可中斷的版本,實例化并提交到線程池:
2.持有鎖時不要執行阻塞操作
原文:www.securecoding.cert.org,LCK09-J.Do not perform operations that canblock while holding a lock。
持有鎖時執行耗時或阻塞操作會嚴重降低系統性能,且可能導致饑餓。此外,死鎖會導致相互依賴的線程無限阻塞。阻塞操作包括網絡、文件、控制臺I/O(如Console.readLine())和對象序列化,無限延遲線程執行也算是一種阻塞操作(如通過Thread.sleep())。因此,在持有鎖時,程序不應該執行阻塞操作。
當Java虛擬機(JVM)與不可靠網絡、文件I/O交互時,可能引起巨大的性能損耗。在這種情況下,應避免持有鎖時執行網絡上的文件I/O操作。在等待輸出流鎖或I/O完成事件的文件操作(如日志)會阻塞,可以在一個單獨的線程中執行來加速任務處理。記錄請求日志可以添加到一個隊列,與直接文件I/O相比,隊列put()操作會增加一點小的開銷。
違規代碼示例(Deferring a Thread)
如下違規代碼示例定義了一個接受timeout參數的工具類:
方法是synchronized,當線程休眠后其他線程不能使用synchronized的方法。當前對象的監視器并未被釋放,這是因為Thread.sleep()?方法沒有同步語義。
合規解決方案(Intrinsic Lock)
如下合規解決方案定義了doSomething()方法,其帶有一個timeout參數而不是time參數。使用Object.wait()代替Thread.sleep(),Object.wait()允許設置一個通知的超時周期,超時后可以喚醒線程。
進入到等待狀態后當前對象的監視器會立即釋放。當超時后,線程會在重新獲取到當前對象的監視器后恢復繼續執行。
根據Object類Java API的描述:
wait方法,它將當前線程放入到該對象的等待集,且只有該對象能釋放鎖;當線程等待時,當前線程上的任何synchronized的其他對象將保持鎖定。
此方法應僅由該對象監視器擁有者線程調用。
程序必須確保持有其他對象鎖的線程在進入等待狀態之前適時釋放這些鎖。等待和通知的一些額外幫助可見?THI03-J. Always invoke wait() and await() methods insidea loop 和 THI02-J. Notify all waiting threads rather than a single thread。
違規代碼示例(Network I/O)
如下違規代碼示例定義了sendPage()方法,其從服務端向客戶端發送一個Page對象。該方法是synchronized,在多線程請求并發訪問時來來保護?pageBuff數組。
public class SendPage {
private final int MAX_PAGE_SIZE = 10;
Page[] pageBuff = new Page[MAX_PAGE_SIZE];
public class Page {
int code;
String message;
Object page;
String pageName;
public String getName() {
// TODO Auto-generated method stub
return pageName;
}
}
public synchronized boolean sendPage(Socket socket, String pageName) throws IOException {
ObjectOutputStream out = new ObjectOutputStream(socket.getOutputStream());
Page targetPage = null;
for (Page page : pageBuff) {
if (page.getName().compareTo(pageName) == 0) {
targetPage = page;
}
}
if (targetPage == null) {
return false;
}
out.writeObject(targetPage);
out.flush();
out.close();
return true;
}
}
在synchronized的sendPage()方法中調用writeObject()會造成延遲和死鎖,像在高延遲網絡或當網絡連接有損(丟包)情況時。
合規解決方案
如下合規解決方案將整個過程拆分為如下步驟:
1.在數據結構上執行的操作需要同步;
2.創建對象的副本用于發送;
3.在單獨的無同步方法中執行網絡調用。
public class SendPageCanStop {
private final int MAX_PAGE_SIZE = 10;
Page[] pageBuff = new Page[MAX_PAGE_SIZE];
public class Page {
int code;
String message;
Object page;
String pageName;
public String getName() {
// TODO Auto-generated method stub
return pageName;
}
}
public boolean sendPage(Socket socket, String pageName) throws IOException {
Page targetPage = getPage(pageName);
if (targetPage == null) {
return false;
}
return deliverPage(socket, targetPage);
}
private synchronized Page getPage(String pageName) {
Page targetPage = null;
for (Page p : pageBuff) {
if (p.getName().compareTo(pageName) == 0) {
targetPage = p;
}
}
return targetPage;
}
private boolean deliverPage(Socket socket, Page page) {
ObjectOutputStream out = null;
boolean result = true;
try {
out = new ObjectOutputStream(socket.getOutputStream());
out.writeObject(page);
out.flush();
out.close();
} catch (Exception e) {
result = false;
} finally {
try {
if (out != null) {
out.close();
}
} catch (Exception e2) {
result = false;
}
}
return result;
}
在該合規解決方案中,無同步的sendPage()方法調用同步的getPage()方法從pageBuff?數組獲取請求的Page。在Page取出后,sendPage()調用無同步的deliverPage()方法將Page交付給客戶端。
總結
以上是生活随笔為你收集整理的java 中断代码_你的java代码可中断吗?(2)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: mysql 设置 character_s
- 下一篇: myeclipse导入项目报错Targe