2017-10-03 1 views
0

안녕하세요.서블릿 세션 추적 - 세션 속성의 스레드 안전

로컬 변수가 선언되고 시작되고 세션 속성에 할당 된 서블릿이 있습니다.

동일한 컴퓨터의 여러 브라우저 창에서 서블릿을 실행할 수 있고 각 창에서 계산을 수행 할 수 있도록 서블릿이 스레드로부터 안전해야하지만 다양한 스레드의 작업이 서로 영향을 미치지 않도록해야합니다. 즉, 계정 잔액 변수가 스레드간에 공유되지 않아 언제든지 모든 스레드에 대해 일관성없는 상태가 발생합니다.

아래 내 코드입니다 : 이제

// Import servlet and HTTP functionality packages. 
import javax.servlet.*; 
import javax.servlet.http.*; 

// Import packages to: handle user inputs and outputs, enable usage of decimal formatting of numbers. 
import java.io.*; 
import java.util.*; 
import java.text.DecimalFormat; 

public class SessionBank extends HttpServlet // Define concrete class and extend HTTP functionality. 
{ 
    public void init() throws ServletException // Initialise variables at start of the servlet. Possible exception. 
    { 

    } 

    // The method to output the initial HTML form to the screen, addressing also possible exceptions. 
    public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException 
    { 
     // Declare and initiate local variables. 
     double balance = 0;      // The current balance variable, un-formatted number, initiate to zero. 
     String formattedBal = "";    // The current balance variable, formatted to show as currency amount, initially blank. 

     // Set balance and formatted balance as session attributes. 
     request.getSession().setAttribute("balance", balance); 
     request.getSession().setAttribute("formattedBal", formattedBal); 

     showBalance(request, response); // Call custom-defined method to display initial page. 
    } 

    // Method to respond to user's button inputs - output relevant HTML back to the screen. Possible exceptions. 
    public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException 
    { 
     HttpSession session = request.getSession(true);  // Establish session object. 

     response.setContentType("text/html");  // Set response type to text/HTML. 
     response.setHeader("Expires", "Tues, 01 Jan 1980 00:00:00 GMT"); // Set past date to forbid cache. 

     // If user clicks "Deposit" button: 
     if(request.getParameter("depButton") != null && request.getParameter("depButton").equals("Deposit")) 
     { 
      if(verifyAmount(request)) // If entered amount passes verification. 
      { 
       addToBalance(request, session);   // Add the amount to current balance. 
       redisplayCurrentPage(response);  // Redisplay initial page. 
      } 
      else  // If entered amount does not pass verification. 
      { 
       showErrorPage(response); // Display error page. 
      } 
     } 

     // If user clicks "Withdraw" button: 
     if(request.getParameter("withdrButton") != null && request.getParameter("withdrButton").equals("Withdraw")) 
     { 
      if(verifyAmount(request)) // If entered amount passes verification. 
      { 
       subtractFromBalance(request, session); // Subtract the amount from current balance. 
       redisplayCurrentPage(response);  // Redisplay initial page. 
      } 
      else  // If entered amount does not pass verification. 
      { 
       showErrorPage(response); // Display error page. 
      } 
     } 

     // If user clicks "Balance" button: 
     if(request.getParameter("balButton") != null && request.getParameter("balButton").equals("Balance")) 
     { 
      showBalance(request, response);  // Display current formatted balance on page. 
     } 
    } 

    private boolean verifyAmount(HttpServletRequest request) // Method to verify entered amount, based on textbook criteria. 
    { 
     boolean amountValid = false; // Declare and initiate a validity variable. 

     // If entered amount is not blank and is greater than zero, return validity as true. Else, return false. 
     if(request.getParameter("amount") != "" && Double.parseDouble(request.getParameter("amount")) > 0) 
      amountValid = true; 
     else 
      amountValid = false; 

     return amountValid;  // Return validity variable. 
    } 

    // Method to add amount to balance, addressing possible exception. 
    private void addToBalance(HttpServletRequest request, HttpSession session) throws IOException 
    { 
     double userAmount = Double.parseDouble(request.getParameter("amount")); // Declare and assign entered amount variable. 

     // Down-cast session attribute object to String, then parse into double type variable. 
     double balOld = Double.parseDouble(String.valueOf(session.getAttribute("balance"))); 

     double balNew = balOld + userAmount;  // Add the amount to current balance and save the value. 

     session.setAttribute("balance", balNew); // Assign new balance to the session attribute. 
    } 

    // Method to subtract from balance. Possible exception. 
    private void subtractFromBalance(HttpServletRequest request, HttpSession session) throws IOException 
    { 
     double userAmount = Double.parseDouble(request.getParameter("amount")); // Declare and assign entered amount value. 

     // Down-cast session attribute object to String, then parse into a double type variable. 
     double balOld = Double.parseDouble(String.valueOf(session.getAttribute("balance"))); 

     double balNew = balOld - userAmount;  // Subtract the amount from the balance and save the value. 

     session.setAttribute("balance", balNew); // Assign new balance value to the session attribute. 
    } 

    private void showBalance(HttpServletRequest request, HttpServletResponse response) throws IOException // Method to output balance HTML page. Possible exception. 
    { 
     PrintWriter out = response.getWriter();  // Establish HTML writer object. 

     formatBalance(request); // Format current balance for displaying. 

     out.println("<html>"); 
      out.println("<hr>");  // Horizontal line. 
      out.println("<title>Online Bank ATM Simulator</title>");  // Title to show on browser title bar. 
      out.println("<h1 align = \"center\">Bank ATM Simulation</h1>"); // Page heading, centered on page. 
      out.println("<body onLoad = \"amount.focus()\">");    // Set focus to the text-field. 
       out.println("<form method = \"POST\" action = \"../servlet/SessionBank\">"); // Form method and submission address. 
        out.println("<center>");  // Tag to center the following output on page. 
        out.println("Amount: "); 
        out.println("<input type = \"text\" name = \"amount\" id = \"amount\" size = \"20\"><br><br>"); // Amount text field. 
        out.println("Balance: "); 
        out.println(request.getSession().getAttribute("formattedBal") + "<br><br>"); // Current formatted balance shown. 
        out.println("<button name = \"balButton\" value = \"Balance\">Balance</button>"); // "Balance" button. 
        out.println("&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"); // Spacers. 
        out.println("<button name = \"depButton\" value = \"Deposit\">Deposit</button>"); // "Deposit" button. 
        out.println("&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"); // Spacers. 
        out.println("<button name = \"withdrButton\" value = \"Withdraw\">Withdraw</button>"); // "Withdraw" button. 
        out.println("</center>");  // Tag to end centering of output on page. 
       out.println("</form>");  // End of form. 
      out.println("</body>"); 
      out.println("<br>"); 
      out.println("<hr>");  // Horizontal line. 
     out.println("</html>"); 
    } 

    // Method to redisplay form after deposit/withdrawal. 
    private void redisplayCurrentPage(HttpServletResponse response) throws IOException 
    { 
     PrintWriter out = response.getWriter();  // Establish HTML writer object. 

     out.println("<html>"); 
      out.println("<hr>");  // Horizontal line. 
      out.println("<title>Online Bank ATM Simulator</title>");  // Title to show on browser title bar. 
      out.println("<h1 align = \"center\">Bank ATM Simulation</h1>"); // Page heading, centered on page. 
      out.println("<body onLoad = \"amount.focus()\">");    // Set focus to the text-field. 
       out.println("<form method = \"POST\" action = \"../servlet/SessionBank\">"); // Form method and submission address. 
        out.println("<center>");  // Tag to center the following output on page. 
        out.println("Amount: "); 
        out.println("<input type = \"text\" name = \"amount\" id = \"amount\" size = \"20\"><br><br>"); // Amount text field. 
        out.println("Balance: "); 
        out.println("<br><br>"); // No formatted balance value shown. 
        out.println("<button name = \"balButton\" value = \"Balance\">Balance</button>"); // "Balance" button. 
        out.println("&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"); // Spacers. 
        out.println("<button name = \"depButton\" value = \"Deposit\">Deposit</button>"); // "Deposit" button. 
        out.println("&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"); // Spacers. 
        out.println("<button name = \"withdrButton\" value = \"Withdraw\">Withdraw</button>"); // "Withdraw" button. 
        out.println("</center>");  // Tag to end centering of output on page. 
       out.println("</form>");  // End of form. 
      out.println("</body>"); 
      out.println("<br>"); 
      out.println("<hr>");  // Horizontal line. 
     out.println("</html>"); 
    } 

    private void formatBalance(HttpServletRequest request) // Method to format the current balance number to a currency amount. 
    { 
     DecimalFormat dollars = new DecimalFormat("$###,###.###"); // Construct new decimal format. 

     // Down-cast session attribute to String, parse to double, then format using the above decimal format and save value. 
     String formattedBal = dollars.format(Double.parseDouble(String.valueOf(request.getSession().getAttribute("balance")))); 

     request.getSession().setAttribute("formattedBal", formattedBal); // Assign new formatted balance to session attribute. 
    } 

    // Method to output error HTML page, if entered amount does not pass verification. Possible exception. 
    private void showErrorPage(HttpServletResponse response) throws IOException 
    { 
     PrintWriter out = response.getWriter();  // Establish HTML writer object. 

     out.println("<html>"); 
      out.println("<head>"); 
       out.println("<title>Amount Input Error</title>"); // Title to show in browser title bar. 
      out.println("</head>"); 

      out.println("<body>"); 
       out.println("<h1>Error processing the input.</h1><br>"); // Heading text. 
       out.println("Please ensure your input:<br><br>"); 
       out.println("- Is not blank.<br>"); 
       out.println("- Is strictly a number.<br>"); 
       out.println("- Is a positive, non-zero amount."); 
      out.println("</body>"); 
     out.println("</html>"); 
    } 

    public void destroy() // Method to terminate the servlet. 
    { 

    } 
} 

, 다중 스레드 (서블릿을 실행하는 브라우저 창)을 사용하여 때, 대신에 무슨 일이 하나 개의 스레드가 덮어 씁니다 균형, 다른 스레드 읽기의 균형이된다 . 물론 부정확합니다. 일관된 결과를 위해 각 변수에 대한 자체 사본을 갖기 위해 각 동시 스레드가 필요합니다.

균형을 클래스 (인스턴스) 변수로 저장하는 것과 달리 로컬로 저장하고 세션 속성에 할당하면 스레드로부터 안전합니다. 그러나 그렇다면 왜 스레드가 서로의 변수를 업데이트합니까? 무엇이 잘못 코딩 되었습니까?

고마워요!

답변

0

브라우저와 약간 일치하지 않을 수 있지만 궁극적으로는 단일 브라우저가 단일 세션 쿠키 (일반적으로 JSESSIONID라고 함)로 서버와 상호 작용한다는 것입니다. 서버와 코드의 관점에서 보면 단일 쿠키로 인해 여러 개의 탭이나 브라우저가 있다는 것을 알 수 없습니다.

이 경우 하나의 스레드가 세션의 값에 액세스하여 업데이트하고 저장합니다. 다른 브라우저 탭을 서비스하는 또 다른 스레드는 동일한 작업을 수행합니다. 그러나 세션에는 하나의 세션과 하나의 변수 만 있으므로 브라우저 탭간에 궁극적으로 공유됩니다.

이 문제를 해결하려면 플랫폼 간 차이가있을 수 있습니다. 한 가지 방법은 브라우저에서 일부 JavaScript가 시작될 때 고유 번호 또는 코드를 생성하는 것입니다. 예를 들어, HTML 코드에 onload 메서드를 만들고 간단한 Math.random() JavaScript 호출을 사용합니다. 그런 다음 모든 요청에 ​​대해이 번호를 백엔드에 전달합니다. 백엔드 쪽에서는 JavaScript 생성 번호를 다른 매개 변수 맵에 매핑하고이를 세션에 저장하는 맵을 작성합니다.

디스플레이 및 백엔드 로직을 모두 한 곳에서 볼 수 있으므로 코드를 구현하기가 더 어려워집니다 (일반적으로 디스플레이 용 JSP와 백엔드 용 서블릿).하지만 완료 할 수 있습니다 .

+0

stdunbar, 빠른 응답을 주셔서 대단히 감사 드리며 같은 브라우저에서 세션 공유가 문제였습니다. 크롬과 Internet Explorer에서 서블릿을 열어 각 테스트가 독립적으로 실행되고 영향을받지 않는 "잔액"변수를 가짐으로써이를 테스트했습니다. 먼저 Chrome의 설정에서 쿠키를 공유하지 않도록 설정하는 방법이 있는지 살펴 보겠습니다. 나는 당신이 제안한 임의의 디스크립터 ID를 생성하기 위해 코드 수정을 진행할 것인지 확신 할 수 없다. 왜냐하면 이것은 할당 질문이고,이 시점까지는이 책과 같이 일부 엑스트라를 다루지 않았기 때문이다. –