线程getName()返回错误的名称

时间:2012-11-28 20:51:04

标签: java multithreading thread-safety

好的,我知道很难理解线程是如何工作的,但是在有人帮助我理解之前我会相信这是一个错误:)

在我的Main类及其main()方法中,我有:

public static void main(String args[]){
    StoneBucket stoneBucket = new StoneBucket();    
    StonePutter spRunnable   = new StonePutter(stoneBucket);
    StoneThrower stRunnable  = new StoneThrower(stoneBucket);
    StoneThrower stRunnable2 = new StoneThrower(stoneBucket);

    //Create the Threads that will take the Runnables as arguments
    Thread puttingThread = new Thread(spRunnable);
    Thread throwingThread = new Thread(stRunnable);
    Thread throwingThread2 = new Thread(stRunnable);

    puttingThread.setName("Putter");
    throwingThread.setName("Thrower 1");
    throwingThread2.setName("Thrower 2");
    [...]

然后在我的StoneThrower课程中我有

public class StoneThrower implements Runnable{

private StoneBucket sb;
private String name;

public StoneThrower(StoneBucket _sb){
    this.sb = _sb;
}   

public void run(){

    name = Thread.currentThread().getName();        
    System.out.println("T::"+name+" started...");       
    int count = 0;      
    while(true){
            [...]

当我编译并运行此代码时,我得到了:

Screenshot of 2 threads...

所以,我的问题是为什么这两个线程都为currentThread().getName()返回相同的名称? 在创建它们时,它们通过threadX.setName("XXX")分配了名称,并通过调用threadX.start()启动了那些可运行的... 有人可以请你澄清一下吗?

编辑:我接受了正确的答案,因为将stRunnable更改为stRunnable2的行为与预期一致。现在真正的问题是为什么会发生这种情况。我创建两个线程并分别启动它们。 run()方法(在创建线程时调用一次)返回错误的名称是怎么回事?

2 个答案:

答案 0 :(得分:9)

这是因为您将线程名称存储在name的实例变量StoneThrower中。由于并发性,第二个线程会覆盖第一个线程刚刚设置的name的值,并且它们都输出相同的值。

以下是您的情景:

1. Thread1#start
2. Thread2#start
3. Thread1#runnable#run -> runnable.name = 'Thrower 1'
4. Thread2#runnable#run -> runnable.name = 'Thrower 2' // overrides
5. Thread1#runnable#run -> System.out.println(runnable.name)
6. Thread2#runnable#run -> System.out.println(runnable.name)

答案 1 :(得分:5)

您使用相同的runnable创建两个线程:

Thread throwingThread = new Thread(stRunnable);
Thread throwingThread2 = new Thread(stRunnable);
                                    ^^^^^^^^^^ stRunnable2?

将线程名称存储在Runnable对象的实例变量中。由于该对象由两个线程共享,因此执行name = Thread.currentThread().getName()的第二个线程会用自己的名称覆盖第一个线程的名称。