Actor模式
多线程太复杂了,让我们回到单线程吧!
在Actor模式中,每个Actor持有自己的状态数据,并且拥有自己的执行线程。
其他对象只能向Actor对象发送消息,Actor对象则在自己的线程中处理消息,查询状态或者修改状态都需要通过发送消息给Actor对象来完成。这个过程类似将信件投递到邮箱中,等待邮差处理。
举个栗子
网络游戏领域,Actor模式非常流程。原因是游戏逻辑太复杂啦,各个模块都可能拥有自己的业务线程,不同模块之间通过发送消息进行通信。
例如:玩家加入场景,场景需要读取玩家的个人信息,如昵称,头像等...
package org.summer.actor;
import cn.hutool.core.thread.NamedThreadFactory;
import lombok.AllArgsConstructor;
import lombok.Data;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class ActorPattern {
public static void main(String[] args) {
Player player = new Player(1L, "Jack");
Scene scene = new Scene();
scene.addPlayer(player);
System.out.println(scene.getScenePlayer(player.getId()));
player.setNickname("Terry");
System.out.println(scene.getScenePlayer(player.getId()));
//如果需要同步,需要再次向场景发布消息更新player信息
scene.addPlayer(player);
System.out.println(scene.getScenePlayer(player.getId()));
}
public static class Player {
private final Long id;
private String nickname;
public Player(Long id, String nickname) {
this.id = id;
this.nickname = nickname;
}
private final ExecutorService service = Executors.newSingleThreadScheduledExecutor(new NamedThreadFactory("playerService", true));
public String getNickname() {
return safeExecute(service, () -> nickname);
}
public void setNickname(String nickname) {
safeExecute(service, () -> {
this.nickname = nickname;
return null;
});
}
public Long getId() {
return id;
}
}
public static class Scene {
private final ExecutorService service = Executors.newSingleThreadScheduledExecutor(new NamedThreadFactory("sceneService", true));
private final Map<Long, ScenePlayer> players = new HashMap<>();
@AllArgsConstructor
@Data
public static class ScenePlayer {
private String nickname;
}
public void addPlayer(Player player) {
safeExecute(service, () -> {
players.put(player.getId(), new ScenePlayer(player.getNickname()));
return null;
});
}
public ScenePlayer getScenePlayer(Long playerId) {
return safeExecute(service, () -> players.get(playerId));
}
}
private static <T> T safeExecute(ExecutorService service, Callable<T> task) {
try {
return service.submit(task).get();
} catch (Exception e) {
throw wrapRuntimeException(e);
}
}
private static RuntimeException wrapRuntimeException(Exception e) {
if (e instanceof ExecutionException) {
Throwable cause = e.getCause();
if (cause instanceof RuntimeException) {
return (RuntimeException)cause;
} else {
return new RuntimeException(cause);
}
} else if (e instanceof RuntimeException) {
return (RuntimeException)e;
} else {
return new RuntimeException(e);
}
}
}