前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Actor 并发控制模型使我想到了王者荣耀对战伤害控制实现

Actor 并发控制模型使我想到了王者荣耀对战伤害控制实现

原创
作者头像
Lorin 洛林
发布2024-01-29 21:03:04
3301
发布2024-01-29 21:03:04
举报
文章被收录于专栏:Java 技术小屋Java 技术小屋

前言

  • 一般来说,我们有两种策略来在并发线程中实现通信:共享内存和消息传递。大多数传统语言,并发线程之间的通信使用的都是共享内存,共享内存最大的问题就是竞争,我们可以使用锁来解决竞争问题,但处理各种锁的问题让人头痛不已。
  • Actor 模型是一种基于消息模型,在 Actor 模型中,一切皆 Actor ;每个 Actor 有自己的状态和行为,但不共享状态,状态由自己维护和修改;Actor 之间通过消息进行通信, 但每个 Actor 同一时间只能处理一条消息,保证每个 Actor 独占式操作,从而巧妙的实现无锁。下面我们来看看 Actor 模型是如何基于消息模型实现无锁并发编程。

Actor 模型

  • Actor 模型是一种并发编程模型,用于处理多线程和分布式系统中的并发问题。它将并发计算分解为独立的、可并行执行的"角色"(Actors),这些角色之间通过消息传递进行通信,从而实现高度并发和分布式计算。
  • Actor 模型的设计理念是将计算单元封装成独立的角色,每个角色都有自己的状态和行为,角色内部状态只能自己修改和维护,同时能够接收和发送消息。

基本概念

Actor 角色

  • Actor 是 Actor 模型的基本单元,代表了一个独立的计算实体。
  • 每个 Actor 有一个唯一的标识符(地址),用于标识和访问该 Actor。
  • 每个 Actor 有自己的状态和行为,但不共享状态,状态由自己维护和修改。

Mailbox(邮箱)

  • 每个 Actor 都有一个 Mailbox(邮箱)用于接收其它 Actor 发送的数据,并等待接收者进行处理。

消息传递

  • 在 Actor 模型中通信通过消息传递实现,一个 Actor 可以向另外一个 Actor 发送消息,接受到消息后接收者会进行一些处理,比如进行一些计算或改变自己状态,但接受到的消息不一定会立即处理而是按照消息接收顺序进行异步处理。

图示

特点

  • 并发性: 每个 Actor 都是独立执行的,可以在不同的线程或进程中并行运行,从而实现高度并发。
  • 解耦性: Actors 之间的通信是松散耦合的,它们不共享状态,只通过消息交互。降低了系统的复杂性,使得系统更容易扩展和维护。
  • 容错性: 由于 Actors 之间相互独立,一个 Actor 的故障不会影响其他 Actors,从而提高了系统的容错性。
  • 分布式: Actor 模型天生支持分布式计算,因为 Actors 可以在不同的节点上运行,通过网络进行消息传递。

适用场景

  • 共享内存模型更适合高吞吐量的、要求低延迟的订单处理场景,因为它允许直接访问共享状态。
  • 而 Actor 模型更适用于需要隔离和异步处理的问题,比如游戏对战,异步通知,电信公司所有基站状态维护管理等场景,而不是高度优化的事务性操作,特别是在需要维护大量共享状态的情况下。
  • 实际使用中大多数场景会混合两种模式使用,比如一些电子商务系统,例如使用共享内存来管理订单的状态和库存,而使用 Actor 模型来处理异步通知、通信和与用户的交互。这种混合使用的方式可以根据具体需求来灵活选择合适的模型。

使用示例

  • 使用 Actor 实现简单双人对战游戏

版本及依赖

  • 版本:Jdk8
  • 依赖
代码语言:java
复制
        <dependency>
            <groupId>com.typesafe.akka</groupId>
            <artifactId>akka-actor_3</artifactId>
            <version>2.8.5</version>
        </dependency>

实现

  • 创建一个表示玩家的 Actor
代码语言:java
复制
import akka.actor.AbstractActor;
import akka.actor.ActorRef;
import akka.actor.Props;

public class PlayerActor extends AbstractActor {
    private String playerName;
    private int score;
    private ActorRef opponent;

    public PlayerActor(String playerName) {
        this.playerName = playerName;
        this.score = 0;
    }

    public static Props props(String playerName) {
        return Props.create(PlayerActor.class, playerName);
    }

    @Override
    public Receive createReceive() {
        return receiveBuilder()
            .match(Attack.class, this::handleAttack)
            .match(Defend.class, this::handleDefend)
            .match(ReportScore.class, this::handleReportScore)
            .match(InitOpponent.class, this::handleInitOpponent)
            .build();
    }

    private void handleAttack(Attack attack) {
        opponent.tell(new Defend(attack.getDamage()), getSelf());
    }

    private void handleDefend(Defend defend) {
        int damage = defend.getDamage();
        score += damage;
        System.out.println(playerName + " defends against an attack and scores " + damage + " points.");
    }

    private void handleReportScore(ReportScore reportScore) {
        getSender().tell(new ScoreResponse(playerName, score), getSelf());
    }

    private void handleInitOpponent(InitOpponent initOpponent) {
        opponent = initOpponent.getOpponent();
    }

    public static class Attack {
        private final int damage;

        public Attack(int damage) {
            this.damage = damage;
        }

        public int getDamage() {
            return damage;
        }
    }

    public static class Defend {
        private final int damage;

        public Defend(int damage) {
            this.damage = damage;
        }

        public int getDamage() {
            return damage;
        }
    }

    public static class ReportScore {
    }

    public static class InitOpponent {
        private final ActorRef opponent;

        public InitOpponent(ActorRef opponent) {
            this.opponent = opponent;
        }

        public ActorRef getOpponent() {
            return opponent;
        }
    }

    public static class ScoreResponse {
        private final String playerName;
        private final int score;

        public ScoreResponse(String playerName, int score) {
            this.playerName = playerName;
            this.score = score;
        }

        public String getPlayerName() {
            return playerName;
        }

        public int getScore() {
            return score;
        }
    }
}
  • 创建一个游戏主控制 Actor,用于初始化和协调玩家
代码语言:java
复制
import akka.actor.AbstractActor;
import akka.actor.ActorRef;
import akka.actor.Props;

public class GameController extends AbstractActor {
    private final ActorRef player1;
    private final ActorRef player2;

    public GameController(ActorRef player1, ActorRef player2) {
        this.player1 = player1;
        this.player2 = player2;

        // 初始化玩家的对手
        player1.tell(new PlayerActor.InitOpponent(player2), getSelf());
        player2.tell(new PlayerActor.InitOpponent(player1), getSelf());
    }

    public static Props props(ActorRef player1, ActorRef player2) {
        return Props.create(GameController.class, player1, player2);
    }

    @Override
    public Receive createReceive() {
        return receiveBuilder()
            .match(PlayerActor.ScoreResponse.class, this::handleScoreResponse)
            .match(PlayerActor.Attack.class, this::handleAttack)
            .build();
    }

    private void handleScoreResponse(PlayerActor.ScoreResponse scoreResponse) {
        System.out.println(scoreResponse.getPlayerName() + " has a score of " + scoreResponse.getScore() + " points.");
    }

    private void handleAttack(PlayerActor.Attack attack) {
        // 随机选择一个玩家发起攻击
        if (Math.random() < 0.5) {
            player1.tell(attack, getSelf());
        } else {
            player2.tell(attack, getSelf());
        }
    }
}
  • 创建一个启动器来启动游戏
代码语言:java
复制
import akka.actor.ActorRef;
import akka.actor.ActorSystem;

public class GameLauncher {
    public static void main(String[] args) {
        ActorSystem system = ActorSystem.create("GameSystem");

        // 创建两个玩家
        ActorRef player1 = system.actorOf(PlayerActor.props("Player 1"));
        ActorRef player2 = system.actorOf(PlayerActor.props("Player 2"));

        // 创建游戏控制器
        ActorRef gameController = system.actorOf(GameController.props(player1, player2));

        // 发起攻击
        gameController.tell(new PlayerActor.Attack(10), ActorRef.noSender());
    }
}

// 运行结果
Player 2 defends against an attack and scores 5 points.
  • 上面示例是一个简单实现,实际项目场景中更加复杂,比如涉及血量、道具、蓝量等等。

个人简介

? 你好,我是 Lorin 洛林,一位 Java 后端技术开发者!座右铭:Technology has the power to make the world a better place.

? 我对技术的热情是我不断学习和分享的动力。我的博客是一个关于Java生态系统、后端开发和最新技术趋势的地方。

? 作为一个 Java 后端技术爱好者,我不仅热衷于探索语言的新特性和技术的深度,还热衷于分享我的见解和最佳实践。我相信知识的分享和社区合作可以帮助我们共同成长。

? 在我的博客上,你将找到关于Java核心概念、JVM 底层技术、常用框架如Spring和Mybatis 、MySQL等数据库管理、RabbitMQ、Rocketmq等消息中间件、性能优化等内容的深入文章。我也将分享一些编程技巧和解决问题的方法,以帮助你更好地掌握Java编程。

? 我鼓励互动和建立社区,因此请留下你的问题、建议或主题请求,让我知道你感兴趣的内容。此外,我将分享最新的互联网和技术资讯,以确保你与技术世界的最新发展保持联系。我期待与你一起在技术之路上前进,一起探讨技术世界的无限可能性。

? 保持关注我的博客,让我们共同追求技术卓越。

我正在参与2024腾讯技术创作特训营第五期有奖征文,快来和我瓜分大奖!

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前言
  • Actor 模型
    • 基本概念
      • Actor 角色
      • Mailbox(邮箱)
      • 消息传递
    • 图示
      • 特点
        • 适用场景
          • 使用示例
            • 版本及依赖
            • 实现
        • 个人简介
        相关产品与服务
        云数据库 MySQL
        腾讯云数据库 MySQL(TencentDB for MySQL)为用户提供安全可靠,性能卓越、易于维护的企业级云数据库服务。其具备6大企业级特性,包括企业级定制内核、企业级高可用、企业级高可靠、企业级安全、企业级扩展以及企业级智能运维。通过使用腾讯云数据库 MySQL,可实现分钟级别的数据库部署、弹性扩展以及全自动化的运维管理,不仅经济实惠,而且稳定可靠,易于运维。
        领券
        问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
        http://www.vxiaotou.com