访问从主线程

时间:2017-03-31 03:18:17

标签: java multithreading

所以,我是线程新手,而且我还在学习一切是如何工作的。所以,我找不到能够解释我的问题的答案(根据我的理解程度)。

我有一个Runnable类看起来像这样:

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

然后我有另一个类执行以下操作:

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

我需要,如果在主线程中,getOk()返回false,请执行某些操作并将其设置为true。 Viceversa,在线程中将其设置为false(无论ok在任何给定时间的值是什么,我都需要继续前进。)

正如此代码所示,我无法在主线程中获得request.getOk()的值。如果我从getter和setter中删除synchronized个单词,我可以访问主线程中的值,直到线程更改它的时间点,并且永远不会再次。

此外,使用执行程序是因为我会创建多个Request对象,并且在访问变量之前等待它关闭会与我执行此操作的原因相矛盾,因为我需要所有线程继续运行。

该线程向服务器发出http请求(随机超时,拒绝响应等)并用于检索某些信息。当线程从服务器获取ok响应和一些信息时,ok变量会记录下来。

如何解决它,以便线程可以更新该变量,但主线程能够在需要时检索它,无论它是否同时被线程更改。

将我的Runnable更改为Callable有用吗?如果是,怎么样?

1 个答案:

答案 0 :(得分:0)

你的例子仍然在线程安全中留下了一些漏洞。如果正确使用,@ Radiodef使用AtomicBoolean提到的内容可以减轻大部分同步。

使用您的示例,这是一个线程安全的Request类,它接受一条消息,就像对http请求的回答一样。

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

你的主要班级:

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

在此实现中,Request类一次只保存一个值。根据您希望考虑使用缓冲区的数据量。

此外,和许多其他人一样,不要使用while (true)!从java concurrent package获取同步对象!

AtomicBoolean对象的更多轻读。