Java 如何在AKKA演员中实现线程安全?
我的项目需要大量的异步编程,所以我选择AKKA平台,就像actor模型一样,可以实现异步系统,就像编写同步代码一样,而不必担心线程问题。在我遇到以下问题(演示代码)之前,一切正常: 首先发送一条“锁定”消息,然后发送一条“解锁”消息,在尝试在收到发送消息时解锁时,抛出了一个Java 如何在AKKA演员中实现线程安全?,java,multithreading,concurrency,thread-safety,akka,Java,Multithreading,Concurrency,Thread Safety,Akka,我的项目需要大量的异步编程,所以我选择AKKA平台,就像actor模型一样,可以实现异步系统,就像编写同步代码一样,而不必担心线程问题。在我遇到以下问题(演示代码)之前,一切正常: 首先发送一条“锁定”消息,然后发送一条“解锁”消息,在尝试在收到发送消息时解锁时,抛出了一个IllegalMonitorStateException,我发现这是由于不同的线程实际处理不同的消息,s->lock.lock()和s->lock.unlock()在不同的线程中执行,因此会抛出IllegalMonitorSt
IllegalMonitorStateException
,我发现这是由于不同的线程实际处理不同的消息,s->lock.lock()
和s->lock.unlock()
在不同的线程中执行,因此会抛出IllegalMonitorStateException
我之前的假设是,参与者的所有操作都在一个线程中执行,因此它是完全线程安全的,不必担心线程问题。当我在我的项目中广泛使用AKKA时,现在我非常担心和不清楚何时需要使用AkkA考虑线程问题。例如,在以下演示代码中:
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();
}
}
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();
}
}
Akka的设计是线程安全的。而且,在一个参与者中永远不需要锁定或同步。不应该这样做 Akka通过一次处理一条消息来实现线程安全。参与者不能同时处理多条消息。但是消息可能会也将在不同的线程中处理。(这是默认行为,但可以使用pin分配器进行更改) 从 不需要诸如synchronized或AtomicInteger之类的并发保护 因为actor实例一次处理一条消息 关于最后的问题 计数和映射的使用方式是线程安全的吗 是的,它是线程安全的 我是否需要使用volatile进行计数并使用ConcurrentHashMap进行映射 不,没有必要这么做。看 用外行的话来说,这意味着 当参与者处理下一条消息时,参与者是可见的。所以 actor中的字段不必是可变的或等效的
Akka的设计是线程安全的。而且,在一个参与者中永远不需要锁定或同步。不应该这样做 Akka通过一次处理一条消息来实现线程安全。参与者不能同时处理多条消息。但是消息可能会也将在不同的线程中处理。(这是默认行为,但可以使用pin分配器进行更改) 从 不需要诸如synchronized或AtomicInteger之类的并发保护 因为actor实例一次处理一条消息 关于最后的问题 计数和映射的使用方式是线程安全的吗 是的,它是线程安全的 我是否需要使用volatile进行计数并使用ConcurrentHashMap进行映射 不,没有必要这么做。看 用外行的话来说,这意味着 当参与者处理下一条消息时,参与者是可见的。所以 actor中的字段不必是可变的或等效的
你可能想用
action3
和action4
代替action2
@IvanStanislavciuc是的,这些都是打字错误,谢谢你的指点。我还编辑了这个问题,添加了更多的信息来说明为什么我需要Actor中的锁。这似乎是一个问题。关于你的编辑,依我看,你不应该这样做。Akka设计为无锁。如果你想利用背压,考虑使用Akka流代替。它可以与参与者集成,因此您的处理仍然可以在参与者中进行。但是您应该创建一个新问题,而不必修改此问题,以避免对此问题进行重大更改。您可能是想使用action3
和action4
而不是action2
@IvanStanislavciuc是的,这些都是打字错误,谢谢您的提醒。我还编辑了这个问题,添加了更多的信息来说明为什么我需要Actor中的锁。这似乎是一个问题。关于你的编辑,依我看,你不应该这样做。Akka设计为无锁。如果你想利用背压,考虑使用Akka流代替。它可以与参与者集成,因此您的处理仍然可以在参与者中进行。但是您应该创建一个新问题,而不必修改此问题,以避免对该问题进行重大更改。
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();
}
}