java - 实现线程安全共享计数器的功能方法

标签 java scala functional-programming thread-safety immutability

我是 Scala 和函数式编程的新手,我喜欢使用不可变对象(immutable对象)可以避免许多线程安全陷阱的想法。一件事仍然困扰着我,它就是用来教授线程安全的经典示例——共享计数器。

我想知道是否有可能实现一个线程安全的计数器(在这个例子中是一个请求计数器),使用不可变对象(immutable对象)和函数概念,并完全避免同步。

因此,这里首先是计数器的经典可变版本供引用 (请原谅公共(public)成员变量,只是为了示例的简洁)

可变的,非线程安全的版本:

public class Servlet extends HttpServlet {

  public int requestCount = 0; 

  @Override
  public void service(ServletRequest req, ServletResponse res) throws ... {
    requestCount++; //thread unsafe
    super.service(req, res);  
  }
}

可变的,经典的线程安全版本:(或者我希望...)

public class Servlet extends HttpServlet {

  public volatile int requestCount = 0;

  @Override
  public void service(ServletRequest req, ServletResponse res) throws ... {
    synchronized (this) {
      requestCount++;
    }
    super.service(req, res);  
  }
}

我想知道是否有一种方法可以使用不可变对象(immutable对象)和 volatile 变量来实现线程安全而无需同步。

所以这是我天真的尝试。这个想法是为计数器设置一个不可变对象(immutable对象),并使用一个易失变量替换对它的引用。感觉有点可疑,但值得一试。

持有人:

public class Incrementer {
  private final int value;
  public Incrementer(final int oldValue) {
    this.value = oldValue + 1;
  }

  public Incrementer() {
    this.value = 0;
  }

  public int getValue() {
    return value;
  }
}

修改后的 servlet:

public class Servlet extends HttpServlet {

  public volatile Incrementer incrementer = new Incrementer();

  @Override
  public void service(ServletRequest req, ServletResponse res) throws ... {
    incrementer = new Incrementer(incrementer.getValue());
    super.service(req, res);
  }
}

我有一种强烈的感觉,这也不是线程安全的,因为我正在从增量器读取,并且可能会得到一个陈旧的值(例如,如果引用已经被另一个线程替换)。如果它确实不是线程安全的,那么我想知道是否有任何“功能性”方法可以在没有锁定/同步的情况下处理这种计数器场景。

所以我的问题是

  1. 这个线程安全吗?
  2. 如果是,为什么?
  3. 如果没有,是否有任何方法可以在不同步的情况下实现这样的计数器?

虽然上面的示例代码是Java的,当然也欢迎Scala的回复

最佳答案

Is this thread safe by any chance?

不,除非您已经在同步块(synchronized block)中创建了不可变对象(immutable对象),否则这不是线程安全的。在线程竞争条件下有可能创建损坏的不可变对象(immutable对象)。

要实现相同的功能,您可以使用 AtomicInteger这避免了显式同步。

public class Servlet extends HttpServlet {

  public AtomicInteger incrementer = new AtomicInteger (0);

  @Override
  public void service(ServletRequest req, ServletResponse res) throws ... {
    int newValue = incrementer.incrementAndGet();
    super.service(req, res);
  }
}

关于java - 实现线程安全共享计数器的功能方法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/16976660/

相关文章:

java - 使用JSP从本地文件系统读取文件

java - 用Java文件方法删除Jar快捷方式

java - PS 到 PDF 转换。 GhostScript 异常 - 无法初始化 Ghostscript 解释器。错误代码为-100

javascript - 如何在 Scalajs 项目中使用 Javascript 库

scala - 从项的迭代器创建子序列的迭代器

scala - 通过文本文件序列化保留Spark分区顺序

unit-testing - 我在哪里可以找到一些关于集成测试的代码示例

java - java中package语句的歧义

compiler-construction - 在 "Real-World"应用程序中使用 ML

javascript - `[e | x <- xs; y <- ys; ...] = concat[[e | y <- ys; ...] | x <- xs]` 怎么样