前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >java是值传递还是引用传递 知乎_按值调用和按引用调用

java是值传递还是引用传递 知乎_按值调用和按引用调用

作者头像
全栈程序员站长
发布于 2022-11-03 08:15:05
发布于 2022-11-03 08:15:05
1.1K00
代码可运行
举报
运行总次数:0
代码可运行

最近整理面试题,整理到值传递、引用传递,到网上搜了一圈,争议很大。带着一脸蒙圈,线上线下查了好多资料。最终有所收获,所以分享给大家,希望能对你有所帮助。 首先说下我的感受,这个题目出的很好,但是在 Java 中这个题目是有问题的(在下面我会解释)。并且,有很多结论是 Java 中只有 值传递。我认为这样说不够严谨。当然如果针对 Java 语言本身来讲,Java 中只有 值传递,没有引用传递,是正确的。但是如果针对 值传递,引用传递定义来说,Java 中还是有引用传递的。下面来分析:

一、值传递、引用传递定义

在深入分析问题之前,先让初问者简单明白一下什么是值传递,引用传递。我先用 Java 代码解释:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public class StringBase {

    public static void main(String[] args) {
        int c = 66; //c 叫做实参
        String d = "hello"; //d 叫做实参

        StringBase stringBase = new StringBase();
        stringBase.test5(c, d); // 此处 c 与 d 叫做实参

        System.out.println("c的值是:" + c + " --- d的值是:" + d);
    }
    
    public void test5(int a, String b) { // a 与 b 叫做形参
        a = 55;
        b = "no";
    }
}

【运行结果】 c的值是:66 — d的值是:hello

可以看出通过方法传递后,int 类型与 String 类型的原值并没有受到前面 test5 方法执行后的影响,还是输出了原值。这种形为通常被说成值传递。如果原值经过 test5 方法后被改变了,这种形为通常被描述为引用传递

定义

值传递:指在调用函数时将实际参数复制一份传递到函数中,这样在函数中如果对参数进行修改,将不会影响到实际参数。 引用传递:是指在调用函数时将实际参数的地址直接传递到函数中(的形参),那么在函数中对参数所进行的修改,将影响到实际参数。 引用传递:形参为指向实参地址的指针,当对形参的指向操作时,就相当于对实参本身进行的操作。(下面文章中 C++ 的定义,我觉得这样说更精简形象一些,所以放了两个定义,其实意思是一样的)

以上,就是相关的定义,大家对这个定义几乎没有分歧,但是我建议大家,有必要去看看 C++ 中 值传递、引用传递的定义。因为在 C++ 中有三个定义:值传递、引用传递、指针传递,推荐一个地址: C++ 值传递、指针传递、引用传递详解

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
//引用传递
void change2(int &n) {
    cout << "引用传递--函数操作地址" << &n << endl;
    n++;
}

我们看上边 C++ 引用传递的代码,使用的 & 操作符。& 操作符在 C++ 中被定义为”引用”,引用在 C++ 中的定义是“引用就是某一变量(目标)的一个别名,对引用的操作与对变量直接操作完全一样”,再看引用其中的一个描述:“声明一个引用,不是新定义了一个变量,它只表示该引用名是目标变量名的一个别名,它本身不是一种数据类型,因此引用本身不占存储单元,系统也不给引用分配存储单元”。因此这引用的概念在 Java 中根本不存在。Java 中哪有给变量起个别名的!!! 因此说,这个题出的就有问题,在 Java 官方中我一直没有找到明确的证据说“Java 中 值传递、引用传递 的定义”我所看到的全是说 C++ 中关于值传递、引用传递的定义。但是,在 Java 中没有 C++ 里”引用”的概念。Java 里只有对象,new 关键字。这就很尴尬了,拿 C++ 中的定义,来解释 Java,我觉得这就是有问题的。问题就出在了引用传递!!! 在 C++ 中关于引用传递的定义明确,代码解释清晰。在 C++ 中引用传递,传递的是一个别名,操作别名就跟操作原值一个样。 然而在 Java 中,没有引用的概念,Java 中只要定义变量就会开辟一个存储单元。因此,对 Java 语言来说只有值传递,没有引用传递是正确的。 虽然 Java 中没有引用(C++ 中 引用”&”)。但是,引用传递的定义,在 Java 中还是有符合条件的。抛开语言中的特性。只针对:值传递、引用传递的定义我们来分析一下,Java 是属于值传递还是引用传递。 要想知道 Java 是属于值传递还是引用传递,这就要从 Java 内存模型聊起了,我们来看基本数据类型与引用类型在内存中的存储方式。

二、基本数据类型、引用类型

1.基本数据类型、引用类型定义 基本数据类:Java 中有八种基本数据类型“byte、short、int、long、float、double、char、boolean” 引用类型:new 创建的实体类、对象、及数组 2.基本数据类型、引用类型在内存中的存储方式 基本数据类型:存放在栈内存中。用完就消失。 引用类型:在栈内存中存放引用堆内存的地址,在堆内存中存储类、对象、数组等。当没用引用指向堆内存中的类、对象、数组时,由 GC回收机制不定期自动清理。 3.基本类型、引用类型内存简单说明图

好,看了基本的内存图,应该能明白 Java 是属于值传递还是引用传递。不明白,也没关系,下面会详细说明,先说引起争议的代码。

三、在 Java 中 值传递 与 引用传递,产生模糊不清的代码

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public class TransmitTest {

    public static void main(String[] args) {

        String a = "hello"; //String 引用数据类型,调用 pass 方法后 b 的值没有改变,不是 hello
        int b = 1; //int 基本数据类型,调用 pass 方法后 a 的值没有改变,还是 1

        User user = new User(); //new Class 引用类型,调用 pass 方法后 name 与 age 的值改变了
        user.setName("main"); // 调用 pass 后,name 为 pass 了
        user.setAge(2); //调用 pass 后,age 为 4 了

        pass(user, a, b); //pass 方法调用

        System.out.println("main 方法 user 是:" + user.toString());
        System.out.println("main 方法 a 的值是:" + a + " --- b 的值是:" + b);
    }

    public static void pass(User user, String a, int b) {

        a = "你好";
        b = 3;

        user.setName("pass");
        user.setAge(4);

        System.out.println("pass 方法 user 是:" + user.toString());
        System.out.println("pass 方法 a 的值是:" + a + " --- b 的值是:" + b);
    }
}

class User {

    String name;
    int age;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "name = " + name + " --- age = " + age;
    }
}

【运行结果】 pass 方法 user 是:name = pass — age = 4 pass 方法 a 的值是:你好 — b 的值是:3 main 方法 user 是:name = pass — age = 4

main 方法 a 的值是:hello — b 的值是:1 结果分析,int b,实参是 1,pass 方法调用后,值还是 1 没变,说明基本数据类型是值传递,大家对这个也几乎没争议。 争议的在下边了: 1.String a 是引用类型,pass 方法调用后,值还是 hello 没变。(结论:好像引用类型也是值传递啊!!!) 2.new User() 也是引用类型,在方法调用后,值居然变了。原值不应该是 name = main,age = 2 的吗?为什么 name = pass,age = 4 了呢?(结论:引用类型好像是引用传递啊???) 这就奇葩了,String 与 new 创建的类,同为引用类型,为什么产生的结果不一样呢?String 不也是一个类吗?User 不也是一个类吗? 有人解释说这个代码比喻的不对,应该用如下代码比喻,在 pass 方法中添加一行代码,user = new User(),pass 方法修改如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public static void pass(User user, String a, int b) {

    a = "你好";
    b = 3;

    user = new User();
    user.setName("pass");
    user.setAge(4);

    System.out.println("pass 方法 user 是:" + user.toString());
    System.out.println("pass 方法 a 的值是:" + a + " --- b 的值是:" + b);
}

【运行结果】 pass 方法 user 是:name = pass — age = 4 pass 方法 a 的值是:你好 — b 的值是:3 main 方法 user 是:name = main — age = 2 main 方法 a 的值是:hello — b 的值是:1

这样一来,改变了形参的值,但是实参没有改变。因此有人得出结论,Java 中只有值传递,没有引用传递。(我并不这么认为,原因如下) 使用 user = new User() 这个代码来做验证,我觉得是符合 String 类型做形参时的验证地,但是,此示例不符合引用传递的验证。 在验证之前,我们先看下使用 user=new User(); 语句之前与之后的内存模型图,能有助于我们更好的验证结果,同时也有助于更好的理解 Java 内存模型。我们看 TransmitTest 类在 Java 内存模型中的存储图:

图1 pass() 方法中没有使用 user=new User() 语句的内存模型图

在 图1 中,main() 方法中的 user 类,与 pass() 方法中的 user 类,指向的是同一个堆内存中的 User 类,红色虚线是在 main() 方法中初次给 name 属性赋的值”main”。实线部分,是在 pass() 方法中给 name 属性赋的值”pass”。因为在堆内存中只有一个 User 类实体,因此 main() 方法与 pass() 方法中的 user 指向的都是同一个 User 类 0x000031。因此,无论在 main() 方法还是 pass() 方法中,改变其 user 的属性值后,打印 User 类的属性值肯定是一样的,他们用的是一个实体类。

图2 pass() 方法中使用了 user=new User() 语句的内存模型图

在 图2 中,main() 方法中的 user 类首次加载,堆内存开辟了一个地址为 0x000031 的 User 类实体。当把 main() 方法中的实参 user 传递给 pass() 方法中形参 user 的时候,栈内存在 pass() 方法区中开辟了一个空间,并引用了地址为 0x000031 的 User 类。此时两个方法中的 User 类其实是一个。 然而当 pass() 方法中的 user=new User()语句执行后,堆内存中新开辟了一个地址为 0x000032 的 User 类,pass() 方法中的 user 从此指向了地址为 0x000032 的 User 类。 因为 pass() 方法 与 main() 方法中的 user 属性分别指向了不同的 User 类,所以两个方法中的 User 类的属性无论怎么修改,相互都不影响。 但是,这种操作是不能验证引用传递定义的。因为实参传值给形参后,形参自己改变了地址,这就和引用传递无关了。我们再来用代码验证。 我们可以使用 C++ 引用传递代码来验证,使用 user = new User() 语句验证引用传递的错误性C++ 中引用传递代码

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
class User
{
public:
    int age;   // 长度
    string name;  // 宽度
};


//引用传递
void pass(User &user) {

    cout << "引用传递 -- user的地址是:" << &user << endl;

    user.age = 2;
    user.name = "你好";
}


int main() {

    User user;
    user.age = 1;
    user.name = "hello";

    cout << "实参 -- user的地址是:" << &user << endl;
    pass(user);
    cout << "实参 -- user的值 age=" << user.age << ",name=" << user.name << endl;

    system("pause");
    return false;
}

【运行结果】 实参 — user的地址是:00DCF768 引用传递 — user的地址是:00DCF768 实参 — user的值 age=2,name=你好

在 C++ 中,引用传递的实参与形参地址一致,在引用的方法中,使用的就是实参的地址。当修改形参值后,实参值也跟着变。现在我们按照 user=new User(); 的方法改变一下引用方法 pass 如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
//引用传递
void pass(User &user) {

    cout << "引用传递 -- user的地址是:" << &user << endl;

    User user2 = user; //相当于 Java 中的 user=new User();
    cout << "引用传递 -- user2的地址是:" << &user2 << endl;

    user2.age = 2;
    user2.name = "你好";
}

【运行结果】 实参 — user的地址是:00CFFACC 引用传递 — user的地址是:00CFFACC 引用传递 — user2的地址是:00CFF9AC 实参 — user的值 age=1,name=hello

我们看,改变引用传递中形参 user 的地址后(后期改变的地址,这跟引用传递,值传递还有什么关系?),再修改形参 user 的值,实参没有任何变化。这就破坏了引用传递的场景,因此不能使用 user=new User(); 语句来验证引用传递的定义。

排除了其他异议,我们再来分析 Java 中有没有引用传递。

先把引用传递的定义放上

引用传递:是指在调用函数时将实际参数的地址直接传递到函数中(的形参),那么在函数中对参数所进行的修改,将影响到实际参数。 引用传递:形参为指向实参地址的指针,当对形参的指向操作时,就相当于对实参本身进行的操作。

经过上面的长篇大论,我想这时候你应该能明白了。在对引用类型做方法传递的时候,是不是先把实参的地址给形参的?之后对形参的操作是,是不是相当于操作实参?最后有没有影响到实际参数? 答案肯定都是有的。

定义关键1:是指在调用函数时将实际参数的地址直接传递到函数中(给形参了) 证明:Java 在进行方法调用传递引用类型参数的时候,就是先给形参一个与实参相同的地址的(此处与 C++ 的不同之处是,C++ 是别名,没有在内存中给形参开辟空间,而 Java 给形参开辟了一个栈内存空间,存放与实参相同的引用地址。但是这与引用传递的定义不违背啊!!!定义可没说形参是否有开辟空间的概念)。

定义关键2:在函数中对参数所进行的修改,将影响到实际参数。 证明:Java 在进行方法调用传递引用类型参数后,修改形参的内容后,就是影响了实参的值。

四、String 与包装类的特殊分析

好了,解决了实例对象,我们再来说 String 与包装类,为什么 String 与包装类作为引用类型,却有值传递的功能,居然没有影响到实参!

原因如下: 我们都知道。String 类型及其他七个包装类,是一群特殊群体。当使用 String a = “hello”; 语句时,相当于执行了 String a = new String(“hello”)。然而在 Java 中每一次 new 都是一次对象的创建。如果你创建的对象在堆中不存在,便会创建一个,如果是新创建的对象,那么地址都会变的,后期改变的地址,这跟引用传递,值传递还有什么关系?

其实 String 型方法参数传值的过程,可以用以下代码来解释,我们先看 String 类型的还原:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
String a = "hello"; //a 相当于实参
String a1 = a; //a1 就相当于形参
a1 = "你好";
System.out.println("a是:" + a + " --- a1是:" + a1);

【运行结果】 a是:hello — a1是:你好

逐步还原解释: String a = “hello”; 在 String 池中检查并创建一个常量:”hello”,给 a 分配一个栈内存,在此存储常量 hello 的地址。 String a1 = a; 给 a1 分配一个栈内存,在此存储常量 hello 的地址。相当于 a 把自己持有的地址,复制给了 a1。

内存图如下

a1 = “你好”; 等同于 a1 = new String(“你好”)。在 String 池中检查是否有 “你好” 的常量。如果有,将 a1 的地址指向 “你好” 的地址。如果 String 池中没有 “你好” 常量,在堆内存中创建 “你好” 常量,并将 a1 地址指向 “你好”。

内存图如下

总结如下:String 类型,在进行方法传参的时候,是先将实参地址,赋值给形参(形参在栈内存中确实新开辟了一个新的内存空间,用于存储地址)。但是当再次给 String 类型的形参赋值(与实参内容不一样的值时),形参地址变了,这就和引用传递无关了。我们可以用 C++ 代码中的引用传递,来验证 String 型的这一特殊情况,代码如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
//引用传递
void pass(string &a, int &b) {

    cout << "引用传递 -- a的地址是:" << &a << " --- b的地址是:" << &b << endl;
    cout << "引用传递 -- a的值是:" << a << " --- b的值是:" << b << endl;

    string c = a; // 相当于 java 中的 new String
    int e = b;

    cout << "引用传递 -- c的地址是:" << &c << " --- e的地址是:" << &e << endl;
    cout << "引用传递 -- c的值是:" << c << " --- e的值是:" << e << endl;

    c = "你好"; //在引用传递中改变形参地址后做修改操作,不影响实参
    e = 2; //在引用传递中改变形参地址后做修改操作,不影响实参
}

int main() {
    string a = "hello";
    int b = 1;

    cout << "实参 -- a的地址是:" << &a << " --- b的地址是:" << &b << endl;

    pass(a, b);

    cout << "实参 -- a的值是:" << a << " --- b的值是:" << b << endl;

    system("pause");
    return false;
}

【运行结果】 实参 — a的地址是:00CFF9CC — b的地址是:00CFF9C0 引用传递 — a的地址是:00CFF9CC — b的地址是:00CFF9C0 引用传递 — a的值是:hello — b的值是:1 引用传递 — c的地址是:00CFF8A0 — e的地址是:00CFF894 引用传递 — c的值是:hello — e的值是:1 实参 — a的值是:hello — b的值是:1

我们看,在 C++ 中的引用传递方法中,改变形参的地址后做修改操作,照样不影响实参的值,这就破坏了引用传递的本质,不能这样比喻。

因此,String 与其他包装类,在做形参的时候,由于他们在赋不同于实参的值时,改变了形参的地址,因此使引用传递,看起来像值传递,其实本质还是引用传递。

五、总结

1.这个题目出的不严谨,但是很好(因为涉及了 Java 内存模型) 2.就 Java 语言本身来说,只有值传递,没有引用传递。 3.根据 值传递,引用传递的定义来说: Java 中的基本类型,属于值传递。 Java 中的引用类型,属于引用传递。 Java 中的 String 及包装类,属于特殊群体,作为形参时,由于每次赋值都相当于重新创建了对象,因此看起来像值传递,但是其特性已经破坏了,值传递、引用传递的定义。因此他们属于引用传递的定义,却表现为值传递。

此题争议很大,我仅分享自己的理解,如有不同结论,欢迎指正,一起共勉!

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员栈长,转载请注明出处:https://javaforall.cn/203463.html原文链接:https://javaforall.cn

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2022年10月23日,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

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

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
【go语言】Goroutines 并发模式(二)
摘要 接上一篇博客,主要是对GO语言中的并发编程模式做一个粗略的归纳总结,文中示例参考自golang conference中的一些演讲和博客,go涉及到的Go语言的语法知识细节将予以略去。搬运自原博客www.nyankosama.com 前言 在Goroutines 并发模式(一)中,我们简单地通过boring函数的例子来粗略地阐述了通过channels来和goroutines交流的方法。在本篇中,我将从pattern的方向出发,通过对boring函数的例子进行各种改写,来讲解几种常见了goroutines
李海彬
2018/03/22
7520
【go语言】Goroutines 并发模式(二)
【go语言】Goroutines 并发模式(一)
摘要 这一篇主要是对GO语言中的并发编程模式做一个粗略的归纳总结,文中示例参考自golang conference中的一些演讲和博客,go涉及到的Go语言的语法知识细节将予以略去。搬运自原博客www.nyankosama.com 前言 由于前一阶段实习中接到的项目的告一段落,不知不觉便多出了许多空余的时间,于是就想总结一下最近因为个人兴趣而学习的一些东西。从这篇文章开始以及后面陆续的几篇关于GO语言的文章,均是博主最近对GO语言学习过程中的一些感悟、总结,类似于学习笔记的东西。记录下来并整理成博客一为对学习
李海彬
2018/03/22
9240
go chan 两种应用模型
    协程完成: 7     协程完成: 8     协程完成: 2     协程完成: 5     协程完成: 4     协程完成: 6     协程完成: 1     协程完成: 0     协程完成: 3     协程完成: 9     主程完成
地球流浪猫
2018/08/02
3300
可能不知道的关于golang 的10件事情
匿名结构体 最常见的匿名用法,不用单独定义一个结构体类型 var config struct { APIKey string OAuthConfig oauth.Config } config.APIKey = "BADC0C0A" 匿名结构体定义时并初始化 data := struct { Title string Users []*User }{
李海彬
2018/03/27
6160
Golang+Redis分布式互斥锁
假设我们的某个业务会涉及数据更新,同时在实际场景中有较大并发量。流程:读取->修改->保存,在不考虑基于DB层的并发处理情况下,这种场景可能对部分数据造成不可预期的执行结果,此时可以考虑使用分布式锁来解决该问题
lestat
2021/05/04
3.2K0
【共识算法】--“raft的实现”
看过之前几期的朋友们应该知道在1号第1期最初的时候就实现过一次raft,但之前实现基本是基于python实现的,这次可结合着PBFT,用golang实现了raft。
帆说区块链
2022/04/26
4960
【共识算法】--“raft的实现”
go的routine使用
package main import ( "fmt" "math/rand" "time" ) func p() { for i := 0; i < 2; i++ { fmt.Println(i) time.Sleep(time.Second * 1) } } func sell(c chan int) { for { num := <-c fmt.Println("sell", num, "bread") } } func produce(c chan int) { for { num := rand.Intn(10) t := time.Duration(num) fmt.Println("product", num, "bread") c <- num time.Sleep(time.Second * t) } } func main() { //go关键字+函数名即可启动一个go routine: go p() var input string fmt.Scanln(&input) fmt.Println("End") //go routine使用channel来进行routine间的通信 //显示结果也证明,channel的使用规则 var c chan int = make(chan int) go sell(c) go produce(c) var input2 string fmt.Scanln(&input2) fmt.Println("end") }
公众号-利志分享
2022/04/25
2250
Go 并发实战 -- sync Cond
go中的sync.Cond也就是condition,是一个条件同步变量,与Java中Object的wait、notify、notifyAll方法或者Condition类的作用比较类似,如果有这方面的基础学习起来会非常简单。其实Java中的JUC包实现的可以是最丰富和易用的了,熟知JUC的话,学习其他语言的并发特性及工具的话会非常简单。
邹志全
2019/07/31
9370
​Golang 并发编程指南
作者:dcguo,腾讯 CSIG 电子签开放平台中心 分享 Golang 并发基础库,扩展以及三方库的一些常见问题、使用介绍和技巧,以及对一些并发库的选择和优化探讨。 go 原生/扩展库 提倡的原则 不要通过共享内存进行通信;相反,通过通信来共享内存。 Goroutine goroutine 并发模型 调度器主要结构 主要调度器结构是 M,P,G M,内核级别线程,goroutine 基于 M 之上,代表执行者,底层线程,物理线程 P,处理器,用来执行 goroutine,因此维护了一个 gorout
腾讯技术工程官方号
2021/12/20
1.4K0
深入Go:使用context包轻松完成并发控制
一次请求到达后台,需要并发启动大量的任务以组合出最终的响应,如何设计实现:一个请求到来之后,X秒超时;超时或遇到错误时立即返回,并取消所有并发任务?其实用了Go context包,这个问题就可以非常优雅自然地解决,并且了解Context之后你会赞叹:“哇,真就该这么设计!”
wenxing
2021/12/14
1.3K0
Go 语言简介(下) - 特性
goroutine GoRoutine主要是使用go关键字来调用函数,你还可以使用匿名函数,如下所示: package main import "fmt" func f(msg string) { fmt.Println(msg) } func main(){ go f("goroutine") go func(msg string) { fmt.Println(msg) }("going") } 我们再来看一个示例,下面的代码中包括很多内容,包括时间处
李海彬
2018/03/22
2.3K0
channel练习题
编写一个程序,其中两个 goroutine 来回传递一个整数十次。当每个 goroutine 接收到整数时打印。每次通过整数都增加。一旦整数等于 10,立刻终止程序。
Michel_Rolle
2024/09/17
3K0
【Go 语言社区】关于select和channel数组的配合使用--转
package main import ( "fmt" "math/rand" "reflect" "time" ) type ( product struct { id int // 生产者序号 val int // 产品 } producer struct { id int // 序号 chnl chan *product } ) var ( producerLi
李海彬
2018/03/19
3.1K0
go语言chan 和 routine活用
这里,我们以游戏中的一个情况为例。比如魔兽世界里的40人团队副本BOSS战,很多玩家同时攻击BOSS,BOSS的血量会进行频繁修改,我们要记录对BOSS的致命一击。常规 对BOSS血量的操作经行加锁,然后修改,每次攻击甚至还会判断怪物是否处于死亡状态以判断客户端是否能施放某个技能,但这样会让一个锁控制40个玩家的攻击操作,会导致攻击,技能等释放的不流畅。所以魔兽世界应该不是采用这种加锁的方式。为什么我这样说,因为做为了一个wower,我们经常会发现,实际BOSS,怪物已经死了,但我的寒冰剑还在半路,结果呢,
李海彬
2018/03/23
6760
Go - 从0学习Go的第一课
2.下载编辑器,Atom在github上是开源的,官网:https://github.com/atom
stark张宇
2023/03/07
3290
Go程序例子(45):Stateful Goroutines
在前一个示例中,我们使用了显式锁定和互斥锁来同步多个 goroutines 之间对共享状态的访问。另一个选择是使用 goroutines 和通道的内建同步功能来实现相同的结果。这种基于通道的方法与 Go 的理念一致,即通过通信来共享内存,并且每个数据片段只能由一个 goroutine 拥有。
用户11078756
2024/12/19
1000
Go程序例子(45):Stateful Goroutines
golang 的channels 行为
当我第一次使用 Go 的 channels 工作的时候,我犯了一个错误,把 channels 考虑为一个数据结构。我把 channels 看作为 goroutines 之间提供自动同步访问的队列。这种结构上的理解导致我写了很多不好且结构复杂的并发代码。
李海彬
2018/10/08
1K0
golang 的channels 行为
Go协程加管道实现异步批量消费调度任务
题目有点绕口 在现实的项目中这么搞的也不常见,里面牵涉多个知识点,整理下就当学习了。
pooky
2021/03/28
2.8K0
Go协程加管道实现异步批量消费调度任务
发布订阅模式:使用 Go 实现简单的事件总线
事件驱动架构是计算机科学中一种高度可扩展的范例。它允许我们可以多方系统异步处理事件。
章为忠学架构
2020/01/22
6.5K2
Golang 语言中 Channel 的使用方式
在「Effective Go」并发章节讲到,“不要通过共享内存进行通信;而是通过通信共享内存”。由此表明 Golang 语言官方鼓励用户使用“通过通信共享内存”的方式并发编程。
frank.
2021/04/30
7230
相关推荐
【go语言】Goroutines 并发模式(二)
更多 >
LV.1
这个人很懒,什么都没有留下~
目录
  • 一、值传递、引用传递定义
  • 定义
  • 二、基本数据类型、引用类型
  • 三、在 Java 中 值传递 与 引用传递,产生模糊不清的代码
  • 四、String 与包装类的特殊分析
  • 五、总结
  • 此题争议很大,我仅分享自己的理解,如有不同结论,欢迎指正,一起共勉!
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档