Java - 使用循环填充一个线程的ArrayList

5
这个问题可能很容易回答,但我就是不明白。 为了找到这个问题的“根源”,我缩小了问题范围,只剩下这段代码: 我试图使用循环来填充一个线程的ArrayList。
public static int u=0;

public void test(){
    while (u <10) {
        synchronized(threadList){
            threadList.add(u, new Thread(){                
                @Override public void run(){                    
                    System.out.println("Thread at Index: " + u);                     
                } 
            });
        }
        u++;            
    }

    threadList.get(2).start();    
}

我想通过在索引“2”上启动线程来测试上面的循环。我期望控制台显示“线程索引为:2”,但实际上是这样显示的:“线程索引为:10”。无论我在“.get(int)”方法中写入哪个整数,我都会收到索引为“10”的结果。

为什么会这样?如何解决?

线程的创建似乎是有效的...那么整数'u'是问题所在吗?

感谢您的任何帮助!


8
“u”是静态的,所以您始终会得到当前值(当程序运行时为“10”)。您没有在任何地方保存创建线程时使用的值。 - Sotirios Delimanolis
如果你的线程列表只会被一个线程(主线程?)访问,那么你不需要使用同步来保护它。 - Raedwald
3个回答

9

当你在run方法中引用u

@Override public void run(){                    
    System.out.println("Thread at Index: " + u);                     
} 

获取了当前的u值。在循环结束时和线程运行时,该值为10

请尝试以下操作

public static int u = 0;

public void test(){
    while (u <10) {
        synchronized(threadList){
            threadList.add(u, new Thread(){     
                int i = u;

                @Override public void run(){                    
                    System.out.println("Thread at Index: " + i);                     
                } 
            });
        }
        u++;            
    }

    threadList.get(2).start();    
}

在您的匿名Thread类中,您将实例字段i设置为调用构造函数时u的值,并在执行run()时打印该值。
您在尚未执行的上下文中引用了u,即线程。当最终调用run()方法时,程序将评估变量u,但在程序执行的这一点上,其值将为10。如果您改为执行上述操作,则变量i将保持该精确时刻的值。这是因为当新的Thread启动并执行时,字段初始化会立即发生,并且u会立即评估。

谢谢您的快速回答。 是的,它有效!谢谢!但我真的不明白为什么它可以使用“int i”。 ..这是因为int u是静态的并在方法外定义,而int i留在方法内部无法从外部访问,因此在循环运行时始终具有某个特定值吗?对不起,我的英语不好。 - SoundOfTheTuner
对于像这样的小型Runnable,进行一些小的改进 - 使实例变量(例如此处的i)为final,以提高代码的清晰度和线程安全性。 - user949300
在这种情况下,线程安全实际上并不适用,因为我们正在处理一个匿名类。除了反射之外,没有其他类可以访问该字段。 - Sotirios Delimanolis
@Sotirios Delimanolis 啊,现在我明白了!非常感谢您的帮助! :) - SoundOfTheTuner
我总是过于担心过早发布(不是真正的术语,但你知道我的意思)。Final 可以解决这个问题。 - user949300
显示剩余2条评论

1
    while (u <10) {
        synchronized(threadList){

            final int u_final = u;

            threadList.add(u, new Thread(){                
                @Override public void run(){                    
                    System.out.println("Thread at Index: " + u_final );
                } 
            });
        }
        u++;            
    }

使用“final”变量,它的值是明确的,因为它永远不会改变。

哦,我明白了,谢谢!所以,如果我理解得没错的话: 如果我使用for循环,会更好吗?因为它带有一个循环变量(例如“int i = 0;”),该变量仅在循环运行时发生变化并保持在此循环中。 - SoundOfTheTuner
@MuSiCkiLL8or,你不能直接在线程中使用for循环索引i,因为它属于不同的作用域。试试看,它无法编译。 - Sotirios Delimanolis

-1

尝试两种方法:

package com.test;

import java.util.ArrayList;

public class TestThread5 {
    public static int u=0;
    public ArrayList<Thread> threadList = new ArrayList<>();
    public void test() throws InterruptedException{
        while (u <10) {
            synchronized(threadList){
                threadList.add(u, new Thread(){                
                    @Override public void run(){                    
                        System.out.println("Thread at Index: " + u);                     
                    } 
                });
            }
            u++;            
        }
        for ( u = 0; u < 10; u++) {
            Thread thread = threadList.get(u); 
            thread.start();
            thread.join();
        }
       // threadList.get(2).start();    
    }
    public static void main(String[] args) throws InterruptedException {
        new TestThread5().test();
    }
}

并且

package com.test;

import java.util.ArrayList;
import java.util.Iterator;

public class TestThread4 {
    public  int u=0;
    public ArrayList<Thread> threadList = new ArrayList<>();
    public void test() throws InterruptedException{
        while (u <10) {
            synchronized(threadList){
                threadList.add(u, new MyThread(u));
            }
            u++;            
        }
        for(Thread thread :threadList){
            thread.start();
            thread.join();
        }
       // ((Thread) threadList.get(2)).start();    
    }
    public static void main(String[] args) throws InterruptedException {
        new TestThread4().test();
    }
}
class MyThread extends Thread{
    private int u;
    public MyThread(int u){
        this.u = u;
    }
    @Override
    public void run() {
        System.out.println("Thread at Index: " + u);  
    }

}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接