Loading [MathJax]/jax/output/CommonHTML/config.js
前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >Actor 并发控制模型使我想到了王者荣耀对战伤害控制实现

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

原创
作者头像
Lorin 洛林
发布于 2024-01-29 13:03:04
发布于 2024-01-29 13:03:04
5041
举报
文章被收录于专栏: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
AI代码解释
复制
        <dependency>
            <groupId>com.typesafe.akka</groupId>
            <artifactId>akka-actor_3</artifactId>
            <version>2.8.5</version>
        </dependency>

实现

  • 创建一个表示玩家的 Actor
代码语言:java
AI代码解释
复制
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
AI代码解释
复制
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
AI代码解释
复制
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 删除。

评论
登录后参与评论
1 条评论
热度
最新
666
666
回复回复点赞举报
推荐阅读
编辑精选文章
换一批
快速入门 Akka Java 指南
Akka 是一个用于在 JVM 上构建高并发、分布式和容错的事件驱动应用程序的运行时工具包。Akka 既可以用于 Java,也可以用于 Scala。本指南通过描述 Java 版本的Hello World示例来介绍 Akka。如果你喜欢将 Akka 与 Scala 结合使用,请切换到「快速入门 Akka Scala 指南」。
CG国斌
2019/05/29
10.8K0
快速入门 Akka Java 指南
Akka 指南 之「第 1 部分: Actor 的体系结构」
使用 Akka 可以让你从为 Actor 系统创建基础设施和编写控制基本行为所需的初级(low-level)代码中解脱出来。为了理解这一点,让我们看看你在代码中创建的 Actors 与 Akka 在内部为你创建和管理的 Actor 之间的关系,Actor 的生命周期和失败处理。
CG国斌
2019/05/26
1.1K0
Akka 指南 之「第 2 部分: 创建第一个 Actor」
随着对 Actor 层次结构和行为的理解,剩下的问题是如何将物联网(IoT)系统的顶级组件映射到 Actor。让代表设备和仪表盘的 Actor 处于顶层是很有吸引力的。相反,我们建议创建一个表示整个应用程序的显式组件。换句话说,我们的物联网系统中只有一个顶级的 Actor。创建和管理设备和仪表板的组件将是此 Actor 的子 Actor。这允许我们将示例用例的体系结构图重构为 Actor 树:
CG国斌
2019/05/26
5900
Akka简单的性能测试
这种方案是采用MQ作为中间的媒介,在服务端采用线程池异步处理任务,处理完成之后将结果发送到MQ中,客户端采用侦听的方式得到结果继续进行处理。
小程故事多
2018/08/22
1.3K0
Akka简单的性能测试
漫谈并发编程:Actor模型
0x00 前言 一般来说有两种策略用来在并发线程中进行通信:共享数据和消息传递。熟悉c和java并发编程的都会比较熟悉共享数据的策略,比如java程序员就会常用到java.util.concurrent包中同步、锁相关的数据结构。 使用共享数据方式的并发编程面临的最大的一个问题就是数据条件竞争(data race)。处理各种锁的问题是让人十分头痛的一件事。 和共享数据方式相比,消息传递机制最大的优点就是不会产生数据竞争状态(data race)。实现消息传递有两种常见的类型:基于channel的消息传递和
木东居士
2018/05/25
3K0
Akka 指南 之「第 3 部分: 使用设备 Actors」
在前面的主题中,我们解释了如何在大范围(in the large)内查看 Actor 系统,也就是说,如何表示组件,如何在层次结构中排列 Actor。在这一部分中,我们将通过实现设备 Actor 来在小范围(in the small)内观察 Actor。
CG国斌
2019/05/26
6320
Akka 指南 之「集群感知路由器」
所有「routers」都可以知道集群中的成员节点,即部署新的路由(routees)或在集群中的节点上查找路由。当一个节点无法访问或离开集群时,该节点的路由将自动从路由器中注销。当新节点加入集群时,会根据配置向路由器添加额外的路由。当一个节点在不可访问之后再次可访问时,也会添加路由。
CG国斌
2019/05/26
1K0
SpringBoot下Akka的简单使用
ActorNormal重写createReceive函数,然后使用receiveBuilder构造一个接收器Receive,然后使用Receive的Match函数,对不同类型的请求进行分别处理;在处理内部可以使用 sender().tell发送返回值给请求者。
Kiba518
2023/09/07
9010
Akka 指南 之「第 4 部分: 使用设备组」
让我们仔细看看用例所需的主要功能。在用于监测家庭温度的完整物联网系统中,将设备传感器连接到系统的步骤可能如下:
CG国斌
2019/05/26
5690
使用Akka实现并发
所以我从一个简单的Java程序开始,运行一个while循环直到EOF,然后进行JDBC调用来存储值。这是需要花一个小时才完成了,但后来我意识到程序的运行时比创建程序花费的时间更长。因此,任务并不像看起来那么容易。那可以做些什么呢?当然,我意识到我需要并行完成任务。
银河1号
2019/05/15
1.5K0
Akka 指南 之「第 5 部分: 查询设备组」
到目前为止,我们所看到的对话模式很简单,因为它们要求 Actor 保持很少或根本就没有状态。明确地:
CG国斌
2019/05/26
1.1K0
Akka事件驱动新选择入门
官网:https://guobinhit.github.io/akka-guide/
疯狂的KK
2023/04/09
5590
Spark集群 + Akka + Kafka + Scala 开发(3) : 开发一个Akka + Spark的应用
前言 在Spark集群 + Akka + Kafka + Scala 开发(1) : 配置开发环境中,我们已经部署好了一个Spark的开发环境。 在Spark集群 + Akka + Kafka + Scala 开发(2) : 开发一个Spark应用中,我们已经写好了一个Spark的应用。 本文的目标是写一个基于akka的scala工程,在一个spark standalone的集群环境中运行。 akka是什么? akka的作用 akka的名字是action kernel的回文。根据官方定义:akka用于r
绿巨人
2018/05/18
1.3K0
Akka 指南 之「集群中的分布式发布订阅」
为了使用分布式发布订阅(Distributed Publish Subscribe),你需要将以下依赖添加到你的项目中:
CG国斌
2019/05/26
1.5K0
Akka 指南 之「集群分片」
为了使用集群分片(Cluster Sharding),你必须在项目中添加如下依赖:
CG国斌
2019/05/26
2.4K0
Akka 指南 之「集群单例」
为了使用集群单例(Cluster Singleton),你必须在项目中添加如下依赖:
CG国斌
2019/05/26
1.1K0
Akka 指南 之「容错」
容错(fault tolerance)概念与 Actor 相关,为了使用这些概念,需要在项目中添加如下依赖:
CG国斌
2019/05/26
9620
Akka事件驱动新选择
在高并发场景解决方案中,多从线程角度出发,以解决线程安全问题,锁范围又需要多业务场景考虑,何时上锁,何时解锁,何时自动过期等,而事件驱动是从执行什么操作驱动的,在软件系统的设计层面,两者关联性不大,一个强调安全,一个强调策略,那么有没有两者结合解决并发编程难的事件驱动解决方案呢?带着场景解决方案我们走进Akka。
疯狂的KK
2023/03/07
1.1K0
Akka事件驱动新选择
Akka 指南 之「Actors」
「Actor Model」为编写并发和分布式系统提供了更高级别的抽象。它减少了开发人员必须处理显式锁和线程管理的问题,使编写正确的并发和并行系统变得更容易。1973 年卡尔·休伊特(Carl Hewitt)在论文中定义了 Actors,然后通过 Erlang 语言所普及,并且在爱立信(Ericsson)成功地建立了高度并发和可靠的电信系统。
CG国斌
2019/05/26
4.3K0
Akka 指南 之「持久化」
为了使用 Akka 持久化(Persistence)功能,你必须在项目中添加如下依赖:
CG国斌
2019/05/29
3.6K0
相关推荐
快速入门 Akka Java 指南
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档