使用Akka框架的参与者之间的Java意外同步行为
我最近学习了Java中的Akka框架。当我练习初学者教程时,我遇到了一个有趣的同步问题 基本上,我的代码使用两个主机以相同的精度计算Pi两次。不同的是,一位大师将产生几个Akka演员来并行计算Pi,而另一位大师将以传统的顺序方式计算Pi。这是为了比较性能使用Akka框架的参与者之间的Java意外同步行为,java,synchronization,locking,akka,pi,Java,Synchronization,Locking,Akka,Pi,我最近学习了Java中的Akka框架。当我练习初学者教程时,我遇到了一个有趣的同步问题 基本上,我的代码使用两个主机以相同的精度计算Pi两次。不同的是,一位大师将产生几个Akka演员来并行计算Pi,而另一位大师将以传统的顺序方式计算Pi。这是为了比较性能 package Citi.CAG.MIFID.AkkaTry; import Citi.CAG.MIFID.Common.AppConfig.AppConfig; import Citi.CAG.MIFID.Common.Log.Loggin
package Citi.CAG.MIFID.AkkaTry;
import Citi.CAG.MIFID.Common.AppConfig.AppConfig;
import Citi.CAG.MIFID.Common.Log.LoggingHandler;
import akka.actor.*;
import akka.event.Logging;
import akka.event.LoggingAdapter;
import akka.japi.Creator;
import akka.japi.pf.ReceiveBuilder;
import akka.routing.ActorRefRoutee;
import akka.routing.RoundRobinRoutingLogic;
import akka.routing.Routee;
import akka.routing.Router;
import com.typesafe.config.ConfigFactory;
import sun.rmi.runtime.Log;
import java.time.Duration;
import java.util.LinkedList;
import java.util.function.Consumer;
import java.util.stream.IntStream;
public class Pi {
private static class Calculator {
private final int index;
private final int trunkSize;
private double result = 0;
private boolean isCalculated = false;
public Calculator(int index, int size) {
this.index = index;
trunkSize = size;
}
public int getIndex() {
return index;
}
public int getTrunkSize() {
return trunkSize;
}
public void calculate() {
for(int i = index * trunkSize; i < (index + 1) * trunkSize; ++i) {
result += 4.0 * (1 - (i % 2) * 2) / (2 * i + 1);
}
isCalculated = true;
}
}
private static class Slave extends AbstractActor {
private double calculatePiFor(int start, int nrOfElements) {
double acc = 0.0;
for (int i = start * nrOfElements; i <= ((start + 1) * nrOfElements - 1); i++) {
acc += 4.0 * (1 - (i % 2) * 2) / (2 * i + 1);
}
return acc;
}
Slave() {
receive(ReceiveBuilder.match(Calculator.class, msg -> {
//msg.result = calculatePiFor(msg.getIndex(), msg.getTrunkSize());
//msg.isCalculated = true;
msg.calculate();
sender().tell(msg, self());
}).build());
}
static Props props() {
return Props.create(Slave.class, Slave::new);
}
}
private static class Master extends AbstractActor {
private int resultCount = 0;
private double result = 0.0d;
private final long startTime = System.currentTimeMillis();
private final ActorRef listener;
private final Router router;
public Master(ActorRef listener, int workerCount, int trunkCount, int trunkSize) {
LinkedList<Routee> actorRefs = new LinkedList<>();
for(int i = 0 ; i < workerCount; ++i) {
ActorRef ar = getContext().actorOf(Props.create(Slave.class), "Slave" + i);
getContext().watch(ar);
actorRefs.add(new ActorRefRoutee(ar));
}
router = new Router(new RoundRobinRoutingLogic(), actorRefs);
this.listener = listener;
receive(ReceiveBuilder.match(Calculator.class, msg -> !msg.isCalculated, msg ->
IntStream.range(0, trunkCount)
.boxed()
.forEach(i -> router.route(new Calculator(i, trunkSize), self())))
.match(Calculator.class, msg -> msg.isCalculated, msg -> {
result += msg.result;
resultCount += 1;
if(resultCount == trunkCount) {
Duration duration = Duration.ofMillis(System.currentTimeMillis() - startTime);
listener.tell(result + " " + duration.toString(), self());
getContext().stop(self());
}
}).build());
}
}
private static class Listener extends AbstractActor {
public Listener() {
receive(ReceiveBuilder.match(String.class, msg -> {
LoggingHandler.info(msg.toString());
//akka system clean up code here, omi
}).build());
}
}
public void run() {
final int workerCount = Integer.parseInt(AppConfig.getProperties("workerCount"));
final int trunkCount = Integer.parseInt(AppConfig.getProperties("trunkCount"));
final int trunkSize = Integer.parseInt(AppConfig.getProperties("trunkSize"));
ActorSystem as = ActorSystem.create("AkkaTry");
final ActorRef listener = as.actorOf(Props.create(Listener.class), "Listener");
ActorRef master = as.actorOf(Props.create(Master.class, () -> new Master(listener, workerCount, trunkCount, trunkSize)), "Master");
ActorRef master2 = as.actorOf(Props.create(Master.class, () -> new Master(listener, 1, 1, trunkCount * trunkSize)), "Master2");
master.tell(new Calculator(0, 0), master);
master2.tell(new Calculator(0, 0), master2);
}
public static void main() {
new Pi().run();
}
}
package Citi.CAG.MIFID.AkkaTry;
导入Citi.CAG.MIFID.Common.AppConfig.AppConfig;
导入Citi.CAG.MIFID.Common.Log.LoggingHandler;
导入akka.actor.*;
导入akka.event.Logging;
导入akka.event.LoggingAdapter;
导入akka.japi.Creator;
导入akka.japi.pf.ReceiveBuilder;
导入akka.routing.ActorRefRoutee;
导入akka.routing.RoundRobinRoutingLogic;
导入akka.routing.Routee;
导入akka.routing.Router;
导入com.typesafe.config.ConfigFactory;
导入sun.rmi.runtime.Log;
导入java.time.Duration;
导入java.util.LinkedList;
导入java.util.function.Consumer;
导入java.util.stream.IntStream;
公共类Pi{
专用静态类计算器{
私有最终整数指数;
私人最终尺寸;
私有双结果=0;
私有布尔值isCalculated=false;
公共计算器(整数索引、整数大小){
这个指数=指数;
树干大小=大小;
}
public int getIndex(){
收益指数;
}
public int getTrunkSize(){
返回长度;
}
公共空间计算(){
对于(int i=索引*中继大小;i<(索引+1)*中继大小;++i){
结果+=4.0*(1-(i%2)*2)/(2*i+1);
}
isCalculated=真;
}
}
私有静态类从扩展AbstractActor{
专用双精度计算器(int start,int nrofements){
双acc=0.0;
对于(int i=开始*n元素;i{
//msg.result=calculatePiFor(msg.getIndex(),msg.getRunksize());
//msg.isCalculated=true;
msg.calculate();
sender().tell(msg,self());
}).build());
}
静态道具道具(){
返回Props.create(Slave.class,Slave::new);
}
}
私有静态类主控扩展AbstractActor{
私有int resultCount=0;
私有双结果=0.0d;
private final long startTime=System.currentTimeMillis();
私有最终ActorRef侦听器;
专用最终路由器;
公共主机(ActorRef侦听器、int-workerCount、int-trunkCount、int-trunkSize){
LinkedList actorRefs=新建LinkedList();
对于(int i=0;i!msg.isCalculated,msg->
IntStream.range(0,trunkCount)
.boxed()
.forEach(i->router.route(新计算器(i,trunkSize),self()))
.match(Calculator.class,msg->msg.isCalculated,msg->{
结果+=msg.result;
结果计数+=1;
if(resultCount==trunkCount){
Duration Duration=持续时间(System.currentTimeMillis()-startTime);
listener.tell(result+“”+duration.toString(),self());
getContext().stop(self());
}
}).build());
}
}
私有静态类侦听器扩展了AbstractActor{
公共侦听器(){
接收(ReceiveBuilder.match)(String.class,msg->{
LoggingHandler.info(msg.toString());
//akka系统清理代码在这里,omi
}).build());
}
}
公开募捐{
final int workerCount=Integer.parseInt(AppConfig.getProperties(“workerCount”);
final int trunkCount=Integer.parseInt(AppConfig.getProperties(“trunkCount”);
final int trunkSize=Integer.parseInt(AppConfig.getProperties(“trunkSize”);
ActorSystem as=ActorSystem.create(“AkkaTry”);
final-ActorRef-listener=as.actorOf(Props.create(listener.class),“listener”);
ActorRef master=as.actorOf(Props.create(master.class,()->新master(listener、workerCount、trunkCount、trunkSize)),“master”);
ActorRef master2=as.actorOf(Props.create(Master.class,()->新Master(listener,1,1,trunkCount*trunkSize)),“master2”);
告诉大师(新计算器(0,0),大师);
master2.tell(新计算器(0,0),master2);
}
公共静态void main(){
新建Pi().run();
}
}
在上面的代码中,我有两种计算方法,一种是在从属类中调用calculatePiFor()方法,另一种是在传入的Calculator类中调用calculate()方法。这种细微的差异会产生显著的性能差异
private static class Calculator {
private final int index;
private final int trunkSize;
private double result = 0;
private boolean isCalculated = false;
public Calculator(int index, int size) {
this.index = index;
trunkSize = size;
}
public int getIndex() {
return index;
}
public int getTrunkSize() {
return trunkSize;
}
public void calculate() {
double result = 0.0d;
for(int i = index * trunkSize; i < (index + 1) * trunkSize; ++i) {
result += 4.0 * (1 - (i % 2) * 2) / (2 * i + 1);
}
isCalculated = true;
this.result = result;
}
}
private static class Slave extends AbstractActor {
private double calculatePiFor(int start, int nrOfElements) {
double acc = 0.0;
for (int i = start * nrOfElements; i <= ((start + 1) * nrOfElements - 1); i++) {
acc += 4.0 * (1 - (i % 2) * 2) / (2 * i + 1);
}
return acc;
}
Slave() {
receive(ReceiveBuilder.match(Calculator.class, msg -> {
//msg.result = calculatePiFor(msg.getIndex(), msg.getTrunkSize());
//msg.isCalculated = true;
msg.calculate();
sender().tell(msg, self());
}).build());
}
static Props props() {
return Props.create(Slave.class, Slave::new);
}
}
私有静态类计算器{
私有最终整数指数;
私人最终尺寸;
私有双结果=0;
私有布尔值isCalculated=false;
公共计算器(整数索引、整数大小){
这个指数=指数;
树干大小=大小;
}
public int getIndex(){
收益指数;
}
public int getTrunkSize(){
返回长度;
}
公共空间计算(){
双结果=0.0d;
对于(int i=索引*中继大小;i<(索引+1)*中继大小;++i){
结果+=4.0*(1-(i%2)*2)/(2*i+1);
}
isCalculated=真;
this.result=结果;
}
}
私有静态类从扩展AbstractActor{
专用双精度计算器(int start,int nrofements){
双acc=0.0;
对于(int i=开始*n元素;i{