5

this question is probably pretty easy to answer but I just don't get it. I reduced my problem until this little piece of code was left in order to find the "origin" of this problem: I'm trying to fill an ArrayList of Threads with a loop.

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();    
}

With the last line I wanted to test the loop above by starting the thread at Index '2'. I'm expecting the console to show "Thread at Index: 2" but instead this is shown: "Thread at Index: 10" No matter which integer I write in the ".get(int)"-method, I receive the index '10'.

Why is that? And how to fix this?

The creation of the threads seems to work...so is the integer 'u' the problem?

I appreciate any kind of help! Thanks in advance!

2
  • 8
    u is static, so you'll always get current value (10 when your program runs). You're not saving the value you used when creating the Thread anywhere. Commented Aug 23, 2013 at 21:43
  • If your thread list is accessed from only one thread (the main thread?) You do not need to synchronize using it. Commented Aug 30, 2014 at 19:55

3 Answers 3

9

When you reference u in your run method

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

The current value of u is retrieved. At the end of your loop and when the thread runs, that value is 10.

Try the following

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();    
}

In your anonymous Thread class, you're setting an instance field i to the value u has when the constructor is called and printing that value when the run() is executed.

You reference u in a context that hasn't been executed yet, a thread. When the run() method eventually gets called, the program will evaluate the variable u, but at that point in the execution of the program, its value will be 10. If, instead, you do the above, the variable i will hold the value at that exact moment. This is because when new Thread gets started and executed, field initialization occurs and u is evaluated right away.

Sign up to request clarification or add additional context in comments.

7 Comments

Thanks for the fast answer. Yes, it works! Thank you! But I don't really understand why it does work with "int i". ..is it because int u is static and defined outside the method and int i stays inside the method an can't be accessed from outside and therefore it has always a certain value while the loops runs? Sorry for my bad english.
Minor improvement for little Runnables like this - make the instance variables (i in this example) final for clarity and thread safety.
@user in this case, thread safety doesn't really apply because we're dealing with an anonymous class. Short of reflection, no other class could access the field.
@Sotirios Delimanolis ahh, now I get it! Thank you so much for your help! :)
I always worry (overmuch) about premature publishing (not the real term, but you know what I mean). Final takes care of that.
|
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++;            
    }

with a final variable, it is clear what its value will be, since it'll never change.

2 Comments

Oh, I see..thanks! So, if I am understanding everything right: Would it have been better if I had used a for-loop since it comes with a loop-variable (for example "int i = 0;") that stays inside this loop forever and changes its value only while the loop is running?
@MuSiCkiLL8or You wouldn't have been able to use the for loop index i in the Thread directly because it belongs to a different scope. Try it, it won't compile.
-1

have a try the two ways:

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();
    }
}

and

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);  
    }

}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.