摘要
最小化 servlet 中同步的使用。因為 servlet 是多線程的,主要代碼路徑的同步會嚴重地且極為有害地影響性能。
建議
servlet 是多線程的。基於 servlet 的應用程序必須認識並適當地處理這一點。如果應用程序有很多大段的代碼是同步的,那麼這個應用程序實際上就變成單線程的,而且吞吐量會顯著下降。
在 servlet 中不出現同步是最佳選擇,然而,如果應用程序設計無法避免同步,那麼請使用“鎖對象(lock Object)”並且鎖定可用性最小的代碼路徑。請不要同步 servlet 的 service 方法或 doGet 以及 doPost 方法。這些方法是主要代碼路徑。同步這些方法或任何這些 servlet 方法之一將鎖定整個 servlet 實例。下列代碼顯示了一個使用“鎖對象”來保護 servlet 實例變量 numberOfRows 的示例。
最小同步代碼路徑
public class BpAllBadThingsServletsV1b extends HttpServlet
{
private int numberOfRows = 0;
private javax.sql.DataSource ds = null;
private Object lockObject = new Object();
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException
{
Connection conn = null;
ResultSet rs = null;
PreparedStatement pStmt = null;
int startingRows = 0;
synchronize(lockObject) {
startingRows = numberOfRows;
}
try
{
String employeeInformation = null;
conn = ds.getConnection("db2admin", "db2admin");
pStmt = conn.prepareStatement
("select * from db2admin.employee");
rs = pStmt.executeQuery();
}
catch (Exception es)
{
// Error handling code here
}
}
}
應被取代的方法
以下代碼顯示如何同步主要代碼路徑來保護稱為 numberOfRows 的 servlet 實例變量。
使用 javax.servlet.SingleThreadModel 仍是另一種保護 servlet 實例變量的方法,但最好還是避免使用這種方法。
下面的圖 1 顯示了同步的性能影響
鎖定主要代碼路徑:過度的同步
public class BpAllBadThingsServletsV1a extends HttpServlet
{
private int numberOfRows = 0;
private javax.sql.DataSource ds = null;
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException
{
Connection conn = null;
ResultSet rs = null;
PreparedStatement pStmt = null;
int startingRows;
try
{
synchronized(this) // Locks out Most of the Servlet Processing
{
startingRows = numberOfRows;
String employeeInformation = null;
conn = ds.getConnection("db2admin", "db2admin");
pStmt = conn.prepareStatement
("select * from db2admin.employee");
rs = pStmt.executeQuery();
}
}
catch (Exception es)
{
// Error handling code here
}
}
}
圖 1 -- 性能影響 -- 同步