duleshi
duleshi

Reputation: 2014

How to change a variable via a worker thread

Is there an elegant way to do that? Or it can always be avoided because one can use a better design patter?

import java.util.ArrayList;
import java.util.List;

public class ForTest {

    List<String> ls = new ArrayList<String>();

    public static void main(String[] args) {
        ForTest forTest=new ForTest();
        System.out.println(forTest.ls.size());

        new Thread(new Worker(forTest.ls)).start();
        //size() does not change at all
        System.out.println(forTest.ls.size());
    }
}

class Worker implements Runnable{
    List<String> list;
    public Worker(List<String> li) {
        this.list = li;
    }

    public void run(){
        this.list.add("newItem");
    }
}

Upvotes: 0

Views: 122

Answers (5)

Ravi K Thapliyal
Ravi K Thapliyal

Reputation: 51711

You need to make your main thread sleep() for a while. The size() is getting called before the new Thread gets a chance to update it.

new Thread(new Worker(forTest.ls)).start();

Thread.sleep(2000);
System.out.println(forTest.ls.size());

An even better way would be to join() on to the worker thread. This would make the main thread automatically wake up when the worker is finished.

Thread worker = new Thread(new Worker(forTest.ls));

worker.start();
worker.join();
System.out.println(forTest.ls.size());

In addition to that make use of a synchronized ArrayList to prevent a race condition if the List would be shared and modified by multiple threads.

List<String> ls = Collections.synchronizedList(new ArrayList<String>());

Upvotes: 2

kares
kares

Reputation: 7166

wait for the new thread to actually start running your code + make forTest final to be able to access it (also use a thread-safe collection - best non-synchronous a.k.a. non-blocking) e.g.

import java.util.Collection;
import java.util.concurrent.ConcurrentLinkedQueue;

public class ForTest {

    Collection<String> ls = new ConcurrentLinkedQueue<String>();

    public static void main(String[] args) throws InterruptedException {
        final ForTest forTest = new ForTest();

        System.out.println(forTest.ls.size());

        int threads = 10;

        for ( int i=0; i<threads; i++ ) {
            new Thread(new Runnable() {

                @Override
                public void run() {
                    forTest.ls.add("newItem");
                }

            }).start();
        }

        Thread.sleep(1000);// wait for it !

        System.out.println(forTest.ls.size()); // 10 unless your threads are really slow
    }

}

Upvotes: 1

oddparity
oddparity

Reputation: 436

Please note that ArrayList is not synchronized, but Vector is. You cannot expect the worker to run just a moment after you started its thread. That's why the list size is not changed yet. I guess this is not your complete example, so it is difficult to help you. (If this was your complete example I would wonder why you bother implementing a multi-threaded solution.)
For knowing when the worker finished you could join the threads.

Upvotes: 1

Taylor
Taylor

Reputation: 4087

You seem to be missing the idea of Threading. Your code will not work because your worker has likely not updated ls by the time you print it. If you're using threading, the threads need to communicate state. This is all quite complex, I suggest you read the java tutorials on threading http://docs.oracle.com/javase/tutorial/essential/concurrency/

Upvotes: 1

assylias
assylias

Reputation: 328618

There are several issues with your code (in particular you use ArrayList which is not thread safe without proper synchronization).

But the most obvious one is that the second println statement is almost always going to be called before your run method has had a chance to be executed.

Upvotes: 2

Related Questions