在编程语言课程中,我的教授举了一个例子。
假设以下代码:
int x=initialization();
Thread T1=new computethread(x);
Thread t2=new evaluatethread(x);
...
...
Thread t100=new evaluatethread(x);
线程t1
到t100
需要变量x
的初始值用于计算,而t1
需要更改x
,其中参数的传输为computethread
,首选evaluatethread
?
我的教授说:
1) call by value result
2) call by ref
首选x发送到computethread
,evaluatethread
,为什么按结果调用或按名称调用或此订单如2)按值调用结果,1)ref不支持调用?
为什么他总结这些更好?
答案 0 :(得分:2)
在Java中只有一个传输模式:按值传递。
像int这样的原语。 double,boolean很明显:传递了值。
堆上的对象也是如此,但是通过值传递的东西是该对象的引用。对象本身位于堆上,而不是堆栈帧上。 Java中没有内置的复制机制,就像C ++一样。
您无法更改引用的值,但如果它具有可变数据,则可以修改它指向的对象的状态。
答案 1 :(得分:0)
我认为你的教授可能为了一个例子而使用了看起来像Java的语法,但正如其他人所说,Java只支持按值传递。但是,如果我们讨论的是一种假设语言,它只是简单地具有相似的语法,那么按值传递会描述所需的语义......
在按值传递结果时,仅在函数完成后才会提交对参数的修改。在完成该功能之前,只对本地副本进行修改。
为了进一步说明,按值传递结果大致相当于按值提供所有输入参数并返回封装所有参数的更新值的对象。调用该函数后,将为调用者中的变量分配这些更新的值。相反,在按值传递时,忽略并丢弃更新的值,并且在逐个引用的情况下,立即将更新分配回原始源。因此,为了使这更具体一点,让我们考虑一下这个例子:
add(x, value):
print 'Called with: ' + x
x = x + value
print 'Modified x: ' + x
main():
x = 0
add(x, 3)
add(x, 2)
print 'Caller x: ' + x
在按值传递时,会打印:
Called with 0
Modified x: 3
Called with 0
Modified x: 2
Caller x: 0
...因为参数是“add”中的副本,并且此副本的更新不会传播回调用方。在pass-by-value-result下,此示例的输出为:
Called with 0
Modified x: 0
Called with 3
Modified x: 3
Caller x: 5
...因为,“add”函数传递了“x”的不可变副本,写入“x”只在函数退出后传播(因此,在“add”中,后续尝试读取值给出原始值而不是修改后的值)。但是,在函数完成之后,会将指定的值返回/提交到原始变量。相比之下,传递参考将输出:
Called with 0
Modified x: 3
Called with 3
Modified x: 5
Caller x: 5
...因为pass-by-reference将变量“x”的内存地址传递给“add”函数,而“add”函数会立即将修改写回此位置。
答案 2 :(得分:0)
如果我做出的假设是正确的,我认为可能并且忽略了与IMO问题无关的语言。
理想的行为是让一个线程修改x
,另外99个线程只对其初始值执行一些计算。
请记住,线程共享相同的地址空间。因此,为了避免线程之间的干扰,您希望它们在参数的本地副本上运行。因此,通过引用进行调用是不可能的(下面提到了“特殊”案例)。
现在因为evaluatethread
不需要返回任何内容,所以他们可以只使用call-by-value。 computethread
需要修改该值,因此需要call-by-value-return
。
另请注意,排除干扰的所有变体也有效。例如,如果您pass-by-reference
仅t1
,则可以,因为没有其他线程会直接触及x
。但只有在你可以保证它是唯一的线程时才可以。一般来说,我不认为这是一个安全的假设。
按名称调用并不好,因为如果computethread
在x
需要其值之前修改evaluatethread
,则在初始化后它不会获得x
但是更新了一个。