避免使用状态变量副本阻止?

时间:2011-06-12 04:29:29

标签: java android multithreading

我想对我最近的多线程概念有所了解。这是:

假设我有以下(伪)类,其run()方法在某些线程上永远消失。其他线程将随机使用Foo更改setState()实例的状态。 run()正在进行的工作只涉及读取状态变量,没有写入,并且在执行while语句期间状态不得更改(例如:在位图上绘图)。

在这种情况下,拥有2个状态变量副本似乎可以防止很多潜在的阻塞(因为如果我只有一个共享状态变量的副本,我将不得不同步while循环中的所有内容(使用{{1并且外部线程可能没有机会改变状态)。代码中断后的问题。

stateLock

这个逻辑中的任何漏洞?是否有“标准化”或更智能的方式来做到这一点?如果有大量的状态变量怎么办?更糟糕的是,如果状态变量是不容易复制的自定义对象(例如在自定义对象的变量是引用的java中),该怎么办?

顺便说一句,这来自我目前在Android中使用SurfaceView的工作。

2 个答案:

答案 0 :(得分:3)

有一种更简单的方法可以解决这个问题:

private volatile float sh1, sh2, sh3;  // note "volatile"

在java内存模型中,允许线程缓存来自其他线程的值。 volatile关键字表示所有线程必须使用相同的变量值(即所有线程都引用变量的相同内存位置)。当与基元一起使用时,这意味着您不需要同步(尽管使用64位基元,其中float 1,您可能不需要同步,具体取决于你的JVM是32位还是64位)

您可能希望观看部分/不一致的更新 - 其中某些sh变量会在另一个线程读取时更新。您可能希望通过更新多个sh变量“atomic”来同步更新以保持一致状态。

答案 1 :(得分:2)

要使所有变量保持同步并避免同步,可以将变量放在不可变对象中并作为整体进行更新。在读取状态时,保持一个这样的状态对象作为局部变量,并且保证在您阅读时没有其他人更新它。

以下是一些示例代码(未经过测试等)。如果未在setState中读取旧值或仅从一个线程访问旧值,则易失性字段就足够了。但是在一般情况下(调用setState的多个线程和新状态取决于旧状态的值),使用AtomicReference确保不会错过任何更新。

class Foo {
    private final AtomicReference<State> state = new AtomicReference<State>(new State(0, 0, 0));

    private void setState(float x1, float x2, float x3) {
        State current;
        State updated;
        do {
            current = state.get();
            // modify the values
            float sh1 = current.sh1 + x1;
            float sh2 = current.sh2 + x2;
            float sh3 = current.sh3 + x3;
            updated = new State(sh1, sh2, sh3);
        } while (!state.compareAndSet(current, updated));
    }

    public void run() {
        while (true) {
            State snapshot = state.get();
            // then do tons of stuff that uses sh1, sh2, sh3 over and over...
        }
    }

    private class State {
        public final float sh1, sh2, sh3;

        State(float sh1, float sh2, float sh3) {
            this.sh1 = sh1;
            this.sh2 = sh2;
            this.sh3 = sh3;
        }
    }
}

以下是更新状态的特殊情况的示例代码,它不依赖于状态的旧值:

class Foo {
    private volatile State state = new State(0, 0, 0);

    private void setState(float sh1, float sh2, float sh3) {
        state = new State(sh1, sh2, sh3);
    }

    public void run() {
        while (true) {
            State snapshot = state;
            // then do tons of stuff that uses sh1, sh2, sh3 over and over...
        }
    }

    private class State {
        public final float sh1, sh2, sh3;

        State(float sh1, float sh2, float sh3) {
            this.sh1 = sh1;
            this.sh2 = sh2;
            this.sh3 = sh3;
        }
    }
}