并发编程之Actor模式

427 阅读1分钟

Actor模式

多线程太复杂了,让我们回到单线程吧!

在Actor模式中,每个Actor持有自己的状态数据,并且拥有自己的执行线程。

其他对象只能向Actor对象发送消息,Actor对象则在自己的线程中处理消息,查询状态或者修改状态都需要通过发送消息给Actor对象来完成。这个过程类似将信件投递到邮箱中,等待邮差处理。

image.png

举个栗子

网络游戏领域,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);
        }
    }

}