如果这不是线程安全的,我可以通过什么方式使其成为线程安全?

时间:2019-02-01 15:48:09

标签: java servlets java-ee concurrency thread-safety

关于HTTPSession,我正在使用Java Servlet,并且没有框架。我已经阅读了有关AtomicReferences的内容,但不清楚如何使用它来使此代码线程安全。我以为可以简单地将updateSessionHashSet标记为同步来提供安全性是否正确?我了解不建议您这样做效率低下。还有什么其他选项和语法?

HTTPServlet内部:

private void updateSessionHashSet(HttpServletRequest req){
  HashSet<String> hs = req.getSession().getAttribute("theHashSet");
  String csv = req.getParameter("theCsv");

  String[] arr = csv.split(",");
  for(int i = 0; i < arr.length; i++){
      hs.add(arr[i].trim());
  }
}

public void doPost(HttpServletRequest  req,HttpServletResponse res) throws IOException {
   updateSessionHashSet(req);
}

2 个答案:

答案 0 :(得分:1)

您所引用的帖子告诉您HTTPSession为共享状态,当多个线程可以对其进行修改时,您必须小心。在这里,线程干扰的机会是,如果您以某种方式有来自同一用户的两个并发POST调用此方法。您可以通过类似的方式处理它

private void updateSessionHashSet(HttpServletRequest req){
    String csv = req.getParameter("theCsv");
    Set<String> hs = new HashSet<String>();
    String[] arr = csv.split(",");
    for(int i = 0; i < arr.length; i++){
        hs.add(arr[i].trim());
    }
    req.getSession().setAttribute("theHashSet", hs);
}

这样,您可以先将新的哈希集放到本地变量中,然后代码自动覆盖对会话的theHashSet属性的引用。如果两个线程正在调用相同的代码,则一个或另一个将获胜,但两个不会混杂在一起(这似乎与您发布的代码可能发生的情况一样)。

或者,您可能希望链接文章上的某些答案建议并同步HttpSession对象,但是由于您正在同步该应用程序实例本地的Java对象,因此您必须注意应用程序分布在多个节点上的情况。同步意味着使用共享锁,并且在JVM之间没有共享。

对于IT部门来说,将应用程序部署到多个实例以避免单点故障确实是很常见的。您可以通过将会话固定到特定服务器来解决同步问题,但这会使负载平衡变得困难。

通过这种方式在HttpSession中存储东西可能不是最佳计划,您应该将其保存在某种数据存储中。具有安全并发的最佳方法是最小化可变状态的数量。 HttpSession不能用作长期存储。

有关Java EE内容的线程安全性的准则,我有一个相关的答案here

答案 1 :(得分:0)

当您的班级中有个共享的可变成员成员时,您应该关心线程安全。

局部变量存储在每个线程自己的堆栈中,因此局部变量永远不会在线程之间共享。这与局部原始变量有关。

案例1,本地线程安全原语:

XCEventGenerator

对对象的本地引用更加复杂。引用本身未共享。但是,引用的对象存储在共享堆中。

如果本地创建的对象永不转义该方法,则它是线程安全的。

案例2,本地线程安全引用:

class Processor {
    public void threadSafeMethod() {
         int count = 0;
         count++;
    }
}

案例3个非线程安全的共享的可变的成员:

class Processor {
    public void threadSafeMethod() {
         StringBuilder sb = new StringBuilder("Text");
         appendPoint(sb);
    }

    public void appendPoint(StringBuilder b) {
         b.append(". ");
    }
}

案例4个线程安全的共享的不可变成员

class Processor {

    private StringBuilder sb; // shared mutable reference

    public void nonThreadSafeMethod() {
         sb = new StringBuilder("Text");
         appendPoint(sb);
    }

    public void appendPoint(StringBuilder b) {
         b.append(". ");
    }
}