1

因此對於一個學校項目,我們創建了一個用戶可以提交水下生活報告等的網站。我們使用簡單的依賴注入(javax.inject)和錯誤檢查模式如下:Servlets + injection -multithreading的問題

ReportService.java

public interface ReportService { 
    public static enum ReportServiceErrorsENUM { 
     DB_FAILURE, WRONG_COORD // etc 
    } 
    public Set<ReportServiceErrorsENUM> getLastErrors(); 
    public int addNewReport(Report report); 
} 

ReportServiceImpl.java

public class ReportServiceImpl implements ReportService { 

    private Set<ReportServiceErrorsENUM> lastErrors; 
    private @Inject ReportDAO reportDAO; 

    @Override 
    public Set<ReportServiceErrorsENUM> getLastErrors() { 
     return this.lastErrors; 
    } 

    @Override 
    public int addNewReport(Report report) { 
     lastErrors= new HashSet<ReportServiceErrorsENUM>();//throw away previous errors 
     UserInput input = report.getUserInput(); 
     if (input.getLatitude() == null) { 
      addError(ReportServiceErrorsENUM.WRONG_COORD); 
     } 
     // etc etc 
     if (reportDAO.insertReport(report) != 0) { 
      // failure inserting the report in the DB 
      addError(ReportServiceErrorsENUM.DB_ERROR); 
     } 
     if (lastErrors.isEmpty()) // if there were no errors 
      return EXIT_SUCCESS; // 0 
     return EXIT_FAILURE; // 1 
    } 
} 

SubmitReportController.java

@WebServlet("/submitreport") 
public class SubmitReportController extends HttpServlet { 
    private static final long serialVersionUID = 1L; 
    private @Inject ReportService reportService; 

    @Override 
    protected void doPost(HttpServletRequest request, 
    HttpServletResponse response) throws ServletException, IOException { 
     Report report = new Report(); 
     // set the report's fields from the HttpServletRequest attributes 
     if(reportService.addNewReport(report) == ReportService.EXIT_FAILURE) { 
      for(ReportServiceErrorsENUM error : reportService.getLastErrors()) 
       // display the errors etc 
     } else { 
      // display confirmation 
     } 
    } 
} 

的想法是,在Servlet控制器調用服務(將其注入),然後檢查服務的返回值,如果有一個錯誤在服務調用getLastErrors() - 通知什麼地方出了錯等用戶現在我剛剛意識到這不是線程安全的 - 在@ Inject'ed ReportService的(ReportService的)將使用servlet

  1. 是它跨越了所有的線程(共享手指)?
  2. 如何改善這種錯誤機制?

感謝

回答

1

通常你會想保留這些變量的servlet在一些容器管理的上下文(通常被稱爲「國家」)。 我會將這些錯誤移動到請求範圍 - 這樣它們就被存儲在請求對象(概念上),並且任何servlet/jsp /無論在同一個請求上工作的任何人都可以看到/編輯它們。 不同的請求意味着不同的數據存儲。使用請求範圍從一個servlet

示例代碼可以在這裏找到:http://www.exampledepot.com/egs/javax.servlet/State.html

+0

不幸的是,這套錯誤屬於服務類 - 我沒有看到從那裏獲取請求的方式(?) – 2012-07-22 19:09:56

+0

處理此問題的最簡單方法如下所示: class ServiceState {private Set lastErrors} 然後在你的servlet中--ServiceState stateForThisRequest = new ...; ReportService。addNewReport(report,stateForThisRequest); 之後你添加stateForThisRequest請求(在一些已知的字符串鍵)供任何其他處理該請求使用(我假設你有一個jsp或東西的顯示?) – radai 2012-07-22 19:16:58

+0

有可能某種方式來注入一些事務或會話上下文到你的服務中(不真正知道CDI - javax.inject),但這樣就足夠了。像Tomasz說的 - 在同一個會話上可能有多個請求 - 這就是爲什麼請求範圍最適合保持這個請求。 – radai 2012-07-22 19:19:06

1

你的設計是線程安全的,也沒有準備好服務器的多個用戶都不是。它不是線程安全的,因爲多個用戶(瀏覽器)可以同時訪問該servlet,並同時訪問lastErrors。 (是的,只有一個servlet實例和你的服務)。 HashSet你使用的是而不是線程安全。

此外,如果兩個不同的人試圖使用相同的應用程序,他們將覆蓋並有權訪問彼此提交的報告(錯誤)。換句話說,所有用戶都共享全局狀態,而每個用戶/會話應該有一個狀態。

通過修復第二個問題(我給了你一個提示:使用HTTPSession),你不可能看到第一個問題。這是因爲很難看到同時訪問同一個會話。但它是可能的(併發AJAX請求,兩個瀏覽器標籤)。記住這一點,但現在要解決更重要的問題。

+0

他可以使用ConcurrentSet來繞過第一個問題。 – 2012-07-22 19:19:32

+0

您是否可以辨別出將服務中的共享變量(基本上是Set lastErrors)移動到Servlet請求範圍的方法,如其他答案中所建議的那樣是理想的。請記住有很多服務使用這種模式。即使我使用同步方法,如果用戶A稱爲addReport,並且此返回的用戶B可以在用戶A調用getLastErrors()(race)之前調用addReport和clear()該錯誤集。而粗暴的同步只會殺死多線程的好處。另外我不確定我是否得到了兩個場景之間的區別 – 2012-07-22 19:19:48

+0

@AmirPashazadeh:請參閱http://stackoverflow.com/questions/6992608 – 2012-07-22 19:30:02