简体   繁体   English

如何在 AKKA Actor 中实现线程安全?

[英]How to be thread safe within an AKKA Actor?

My project needs a lot of asynchronous programming so I choose the AKKA platform as with the actor model one can implement asynchronous system just like writing synchronous code without worrying about thread issues.我的项目需要大量的异步编程,所以我选择了AKKA平台,和演员model一样可以实现异步系统,就像写同步代码一样,不用担心线程问题。 Everything works alright till I meet the following issue(demo code):一切正常,直到我遇到以下问题(演示代码):

import akka.actor.AbstractActor;
import akka.japi.pf.ReceiveBuilder;
import java.util.concurrent.locks.ReentrantLock;

public class TestActor extends AbstractActor {
    private final ReentrantLock lock  = new ReentrantLock();

    @Override
    public Receive createReceive() {
        return ReceiveBuilder.create()
                .matchEquals("lock", s -> lock.lock())
                .matchEquals("unlock", s -> lock.unlock())
                .build();
    }
}

first a "lock" message is send, then a "unlock" message is send, in trying to unlock upon receiving the send message, a IllegalMonitorStateException is thrown, I found out that this is due different messages are actually handled by different threads, s -> lock.lock() and s -> lock.unlock() are executed in different threads so IllegalMonitorStateException is thrown.首先发送“锁定”消息,然后发送“解锁”消息,在收到发送消息后尝试解锁时,抛出IllegalMonitorStateException ,我发现这是由于不同的消息实际上是由不同的线程处理的, s -> lock.lock()s -> lock.unlock()在不同的线程中执行,因此抛出IllegalMonitorStateException

My previous assumption is that all actions of an actor is executed in one thread so it is totally thread safe, one dose not have to worry about thread issues.我之前的假设是演员的所有动作都在一个线程中执行,因此它是完全线程安全的,不必担心线程问题。 As I use AKKA extensively in my project, now I'm quite concerned and unclear as to when dose one needs to consider thread issue in using AKKA.当我在我的项目中广泛使用 AKKA 时,现在我非常担心并且不清楚何时需要在使用 AKKA 时考虑线程问题。 For example, in following demo code:例如,在以下演示代码中:

public class TestActor1 extends AbstractActor {
    private int count  = 0;
    private Map<Integer, Integer> map = new HashMap<>();
    
    @Override
    public Receive createReceive() {
        return ReceiveBuilder.create()
                .matchEquals("action1", s -> count++)
                .matchEquals("action2", s -> getSender().tell(count, getSelf()))
                .matchEquals("action3", s -> map.put(3, 2))
                .matchEquals("action4", s -> getSender().tell(map.get(3), getSelf()))
                .build();
    }
}

is the way count and map are used thread safe? countmap的使用方式是线程安全的吗? Do I need to use volatile for count and use ConcurrentHashMap for map ?我是否需要使用volatile进行count并将ConcurrentHashMap用于map

ps ====================== ps =======================

The following demo code demonstrates why I need lock in actor, basically I'm implementing a pipeline with back pressure control, once an actor receives too much tasks from upstream actor, it sends an backPressureHi message to the upstream actor to stall the upstream actor execution loop till the back pressure returns to normal and sends a backPressureNormal to resume:下面的demo代码演示了为什么我需要锁定backPressureHi循环直到背压恢复正常并发送backPressureNormal以恢复:

public class PipeLineActor extends AbstractActor {
    private final ReentrantLock stallLock = new ReentrantLock();

    private Thread executionLoop = new Thread(() -> {
        while (true){
            stallLock.lock();
            stallLock.unlock();
            
            // issue tasks to down stream actors
        }
    });

    @Override
    public Receive createReceive() {
        return ReceiveBuilder.create()
                // down stream actor send "backPressureHi" when back pressure is high to stall the executionLoop
                .matchEquals("backPressureHi", s -> stallLock.lock())
                // down stream actor send "backPressureNormal" when back pressure resumed normal to resume the executionLoop
                .matchEquals("backPressureNormal", s -> stallLock.unlock())
                .build();
    }
}

Akka is designed to be thread safe. Akka 被设计为线程安全的。 And there is never a need for a locking or synchronisation within an actor.并且从不需要在参与者内进行锁定或同步。 It should not be done.不应该这样做。

Akka achieves thread safety by processing a single message at a time. Akka 通过一次处理一条消息来实现线程安全。 An actor cannot process multiple messages simultaneously.一个actor不能同时处理多个消息。 But messages may and will be processed within different threads.但是消息可能并且将在不同的线程中处理。 (this is the default behaviour but can be changed with a pin dispatcher for example). (这是默认行为,但可以使用 pin 调度程序进行更改)。

From documentation文档

No concurrency guards such as synchronized or AtomicInteger are needed since an actor instance processes one message at a time.由于参与者实例一次处理一条消息,因此不需要同步或 AtomicInteger 等并发保护。

To your final questions,对于你最后的问题,

is the way count and map are used thread safe? count 和 map 的使用方式是线程安全的吗?

Yes, it is thread safe.是的,它是线程安全的。

Do I need to use volatile for count and use ConcurrentHashMap for map?我是否需要使用 volatile 进行计数并将 ConcurrentHashMap 用于 map?

No there is no need to do it.不,没有必要这样做。 See Akka and the Java Memory ModelAkka 和 Java Memory ZA559B87068921EEC0584086CE54Z59E

In layman's terms this means that changes to internal fields of the actor are visible when the next message is processed by that actor.通俗地说,这意味着当该参与者处理下一条消息时,该参与者的内部字段的更改是可见的。 So fields in your actor need not be volatile or equivalent.所以你的actor中的字段不需要是可变的或等效的。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM