简体   繁体   中英

Access thread variable that changes from main thread

So, I am new to threads, and I'm still learning how everything works. So, I couldn't find an answer that would provide an explanation for my problem (to my level of understanding).

I have a Runnable class that looks like so:

public class Request implements Runnable {
    private Boolean ok = true;

    public synchronized void setOk(Boolean ok) {
        this.ok = ok;
    }

    public synchronized Boolean getOk() {
        return ok;
    }

    private synchronized void foo() {
        //if something happens
        setOk(false);
    }

    @Override
    public void run() {
        while (true)
            foo();
    }
}

And then I have another class that does the following:

private static Request request;

private static void spawnThreads() {
    ExecutorService e = new Executors.newFixedThreadPool(4);
    request = new Request();

    e.execute(request); 
}

public static void main(String[] args) {
    spawnThreads();

    while (true) {
        System.out.println(request.getOk());
        if (!request.getOk())
            request.setOk(true);

        TimeUnit.SECONDS.sleep(10);
    }
}

I need that if in the main thread, that getOk() returns false , do something and set it to true . Viceversa, set it to false in the thread (which I need to keep on going, no matter what the value of ok is at any given time).

As this code is, I can't get the value of request.getOk() in the main thread. If I remove the synchronized words from the getter and setter, I can access the value in the main thread until a point in time when it is changed by the thread, and never again.

Also, the executor is used because I would create multiple Request objects, and waiting for it to shutdown before accessing the variable would contradict my reason for doing this, as I would need all the threads to keep running.

That thread is making http requests to a server (that randomly times out, denies response, etc) and is used to retrieve some information. The ok variable is there to take a note when the thread acquires an ok response and some information from the server.

How do I solve it so that the thread can update that variable, but the main thread to be able to retrieve it whenever needed, no matter if it was changed by the thread in the meanwhile or not.

Would changing my Runnable to a Callable help? If yes, how?

Your example still leaves some holes in the thread-safety. Like mentioned by @Radiodef using AtomicBoolean can relieve you of most of the synchronisation if used properly.

Using your example, this is a thread safe Request class that accepts a message, like an answer to a http request.

public final class Request implements Runnable {
    private final AtomicBoolean ok = new AtomicBoolean(false);

    // volatile variables promote reference changes through all threads
    private volatile String msg;

    private boolean setMessage(String responseMessage) {
        if (this.ok.compareAndSet(false, true)) {
            this.msg = msg;
            return true;
        }
        return false;
    }

    public boolean hasMessage() {
        // *pure* getters don't need synchronisation!
        return this.ok.get();
    }

    public String getMessageAndReset() {
        // make a copy before resetting the OK
        String msgCopy = this.msg;
        this.ok.compareAndSet(true, false);
        return msgCopy;
    }

    public void run() {
        final Random rand = new Random();
        try {
            while(true) {
                // sleep at random max 5 seconds
                // (simulate unpredictable network)
                TimeUnit.SECONDS.sleep(rand.nextInt(5));
                while(!setMessage("Incoming message")) {
                    // busy waiting ... waits until the current value has   
                    // been retrieved by the main thread
                    Thread.sleep(100);
                }
            }
        } catch (Exception e) {
            System.out.println(e);
        }
    }
}

And your main class:

public final class MainClazz implements Runnable {
    private final ExecutorService exec;
    private final Request request;

    public void MainClazz() {
        this.exec = new Executors.newFixedThreadPool(4);
        this.request = new Request();

        this.exec.execute(request);
    }

    public void run() {
        while (true) {
            if (request.hasMessage()) {
                System.out.println(request.getMessageAndReset());
            }
        TimeUnit.SECONDS.sleep(10);
    }

    public static void main(String[] args) {
        MainClazz main = new MainClazz();
        main.run();
    }
}

In this implementation, the Request class only holds a single value at a time. Depending the amount of data you expect you might want to think about using a buffer.

Also, like many others have mentioned, don't use while (true) ! Get a synchronisation object from the java concurrent package !

More light reading on the AtomicBoolean object.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM