实现线程安全共享计数器的功能方式 [英] Functional way to implement a thread safe shared counter

查看:115
本文介绍了实现线程安全共享计数器的功能方式的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我对Scala和函数式编程相对比较陌生,我喜欢使用不可变对象的想法,我可以避免很多线程安全缺陷。有一件事仍然困扰着我,这是用来教导线程安全的经典例子 - 共享计数器。



我想知道是否可以使用不可变对象和函数概念来实现线程安全计数器(本例中为请求计数器),并避免同步完全。

所以这里的参考首先是计数器
的古典可变版本(对于公共成员变量请原谅,只是为了简化示例)

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

  public class Servlet extends HttpServlet {

public int requestCount = 0;

@Override
public void service(ServletRequest req,ServletResponse res)throws ... {
requestCount ++; //线程不安全
super.service(req,res);





$ b

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

  public class Servlet extends HttpServlet {

public volatile int requestCount = 0;
$ b $ @Override
public void service(ServletRequest req,ServletResponse res)throws ... {
synchronized(this){
requestCount ++;
}
super.service(req,res);


$ / code $ / pre

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


所以这是我的天真尝试。这个想法是为计数器提供一个不可变的对象,并使用volatile变量替换引用。感觉腥,但值得一试。



持有人:

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

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

public int getValue(){
返回值;





$ b

已修改的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);
}
}

我有强烈的感觉,这也不是线程安全的,因为我正在读取增量,并可能会得到一个陈旧的值(例如,如果引用已被另一个线程取代)。如果它确实不是线程安全的,那么我不知道是否有任何功能方式来处理这样的计数器情况,而不锁定/同步。

所以我的问题(b)是


  1. 这个线程是否安全?

  2. 如果是,为什么?


  3. $ b 如果没有,有没有以任何方式实现这样一个计数器而不同步? >虽然上面的示例代码是用Java编写的,但在Scala中的回复当然也是可以的。 这个线程是否安全?


    不,除非您已经在同步块中创建了不可变对象,否则这不是线程-安全。在线程争用条件下有可能创建一个损坏的不可变对象。



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

      public class Servlet extends HttpServlet {

    public AtomicInteger increment = new AtomicInteger(0);
    $ b $ @Override
    public void service(ServletRequest req,ServletResponse res)throws ... {
    int newValue = incrementer.incrementAndGet();
    super.service(req,res);
    }
    }


    I'm relatively new to Scala and functional programming, and I like the idea that using immutable objects I can avoid many thread safety pitfalls. One thing still haunts me, and it's the classical example used to teach thread safety - the shared counter.

    I was wondering if it would be possible to implement a thread-safe counter (a request counter in this example), using immutable objects, and functional concepts, and avoid synchronization completely.

    So for reference here are first the classical mutable versions of the counter (excuse me for the public member variable, just for brevity of the examples)

    Mutable, Non thread safe version:

    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);  
      }
    }
    

    Mutable, Classic thread safe version: (or so I hope...)

    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);  
      }
    }
    

    I was wondering if there is a way using immutable objects, and volatile variables to achieve thread safety without synchronization.

    So here was my naive attempt. The idea is to have an immutable object for the counter, and just replace the reference to it, using a volatile variable. Feels fishy, but worth a shot.

    Holder:

    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;
      }
    }
    

    Modified 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);
      }
    }
    

    I have a strong feeling this is also not thread safe, as I'm reading from incrementer, and might get a stale value (e.g. if the reference was already replaced by another thread). In case it's indeed not thread safe, then I wonder if there is at all any "functional" way to handle such a counter scenario without locking / synchronization.

    So my question(s) are

    1. Is this thread safe by any chance?
    2. If yes, why?
    3. If not, is there at all any way to implement such a counter without synchronizing?

    Although the example code above is in Java, replies in Scala are of course also welcome

    解决方案

    Is this thread safe by any chance?

    No, unless you have already created the immutable object in a synchronized block, this is not thread-safe. There are chances of creating a corrupt immutable object under thread race condition.

    And to achieve the same functionality you can use AtomicInteger which avoids explicit synchronization.

    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);
      }
    }
    

    这篇关于实现线程安全共享计数器的功能方式的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆