我是 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);
}
}
我有一种强烈的感觉,这也不是线程安全的,因为我正在从增量器读取,并且可能会得到一个陈旧的值(例如,如果引用已经被另一个线程替换)。如果它确实不是线程安全的,那么我想知道是否有任何“功能性”方法可以在没有锁定/同步的情况下处理这种计数器场景。
所以我的问题是
- 这个线程安全吗?
- 如果是,为什么?
- 如果没有,是否有任何方法可以在不同步的情况下实现这样的计数器?
虽然上面的示例代码是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/