Java 为什么在方法调用期间将变量置为null会使传递给方法的值也为null?

Java 为什么在方法调用期间将变量置为null会使传递给方法的值也为null?,java,methods,concurrency,parameters,Java,Methods,Concurrency,Parameters,我对一些java行为感到非常困惑,特别是因为我以前对java的理解是,它严格按照值传递 如果我将一个对象传递给一个方法,然后在调用线程中将该对象设为null,我希望该对象仍然存在并且能够在该方法中操作,但事实并非如此 以下是一些示例代码: 公共类方法测试{ 布尔布尔; 布尔二次布尔; 测试列表; 公共静态void main(字符串[]args){ 新建methodTest().run(); } 公开募捐{ 列表=新测试(); 新线程(()->{ func(列表); }).start(); 列表=

我对一些java行为感到非常困惑,特别是因为我以前对java的理解是,它严格按照值传递

如果我将一个对象传递给一个方法,然后在调用线程中将该对象设为null,我希望该对象仍然存在并且能够在该方法中操作,但事实并非如此

以下是一些示例代码:

公共类方法测试{
布尔布尔;
布尔二次布尔;
测试列表;
公共静态void main(字符串[]args){
新建methodTest().run();
}
公开募捐{
列表=新测试();
新线程(()->{
func(列表);
}).start();
列表=空;
布尔=真;
而(!secondBool){
//挡块
}
gc();
}
公共无效函数(大测试){
而(!bool){
//挡块
}
System.out.println(大);
secondBool=true;
}
课堂测试{
@凌驾
受保护的void finalize()抛出可丢弃的{
System.out.println(“我正在清理!”;
}
@凌驾
公共字符串toString(){
返回“Test!”;
}
}
}
此代码打印

null
I'm getting cleaned up!
当我希望它打印时:

Test!
I'm getting cleaned up!
最后,我在类中包含了垃圾收集调用和finalize方法,以确保在函数打印变量
list
之前,gc不会到达该变量,但没有它也会产生同样的效果

这种行为的解释是什么

编辑:


另一个例子是,如果您将run方法中的
list=null
行更改为
list=new Test()
,然后稍微修改
toString
,以计算
Test
实例的数量。程序将打印“Test2”而不是“Test1”,因为
func
中的参数值在调用线程中被重写,即使在我对java的传递值系统的理解中,这不应该发生。

如果我正确记住java,它会更改用户定义类的值。因此,如果您使用基本数据类型和已定义的类,如int、string等,则不会导致此问题。但这里您使用的是您自己的类,因此出于某种原因,它是通过引用传递的

它打印空值,因为存在争用条件,并且新线程会丢失大部分时间

时间是这样的:

  • 主线程(运行
    methodTest.run()
    )创建一个新的
    thread
    对象并启动新线程(意味着它创建一个新的linux/windows线程并通知它可以开始运行)
  • 作为下一步,它将实例变量
    list
    设置为null
  • 与上面的步骤2并行,第二个线程开始运行并最终到达lambda
    ()->{func(list);}
  • 仅当第二个线程执行
    func(列表)时它将读取实例变量
    列表
  • 因此,作为参数
    big
    传递到
    func
    的内容完全取决于

    • 主线程需要多少时间执行
      list=null
    • 操作系统开始执行第二个线程以及第二个线程到达调用
      func(列表)的点所需的时间

    根据您的观察,第二个线程需要更多的时间才能到达
    func(列表)的点的执行超过了主线程需要执行的
    list=null

    之所以发生这种情况,是因为线程尚未调用
    func()
    ,而此时您已将
    列表设置为null,因此它在运行时收到了
    null
    。这里真正的问题是为什么要将
    列表
    置零?简单回答:不要。我甚至没有想到调用函数和将
    list
    设置为null之间的竞争。我会做些测试,然后再打给你。