问题描述
|
我想对我最近的多线程想法有所了解。开始:
假设我有以下(伪)类,其class0ѭ方法正在某个线程上永久消失。其他线程将在随机时间使用
setState()
更改Foo
实例的状态。 run()
所做的工作仅涉及读取状态变量,无需写入,并且在执行while语句的过程中状态不得更改(例如:在位图上绘制)。
在这种情况下,拥有2个状态变量副本似乎可以防止很多潜在的阻塞(因为如果我只有一个共享状态变量副本,则我将必须同步while循环中的所有内容(使用stateLock
),并且外部线程可能无法获得更改状态的机会)。代码中断后的问题。
class Foo {
Object stateLock = new Object();
private float my1,my2,my3;
private float sh1,sh2,sh3; // sh stands for shared
public void setState(...) {
synchronized (stateLock) {
// modify sh1,or sh3 here
}
}
private void updateState() {
synchronized (stateLock) {
// set my1=sh1,my2=sh2,my3=sh3
}
}
public void run() {
while(true) {
updateState();
// then do tons of stuff that uses my1,my3 over and over...
...
}
}
}
这个逻辑有漏洞吗?是否有“标准化”或更聪明的方式来做到这一点?如果有很多状态变量怎么办?更糟糕的是,如果状态变量是不容易复制的自定义对象(例如在Java中,自定义对象的变量是引用)?
顺便说一句,这来自我当前在Android中使用SurfaceView的工作。
解决方法
为了使所有变量保持同步并避免同步,您可以将变量放入不可变对象中,并整体进行更新。读取状态时,请保留一个这样的状态对象作为局部变量,这样可以保证在读取状态时没有其他人更新它。
这是一些示例代码(未经测试等)。如果旧值未在
setState
中读取或仅从一个线程访问,则一个volatile字段就足够了。但是在一般情况下(调用setState和新状态的多个线程取决于旧状态的值),使用AtomicReference可以确保不会丢失任何更新。
class Foo {
private final AtomicReference<State> state = new AtomicReference<State>(new State(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,sh3 over and over...
}
}
private class State {
public final float sh1,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);
private void setState(float sh1,float sh3) {
state = new State(sh1,sh3);
}
public void run() {
while (true) {
State snapshot = state;
// then do tons of stuff that uses sh1,float sh3) {
this.sh1 = sh1;
this.sh2 = sh2;
this.sh3 = sh3;
}
}
}
, 有一个更简单的解决方法:
private volatile float sh1,sh3; // note \"volatile\"
在Java内存模型中,允许线程缓存其他线程的值。
关键字“ 10”表示所有线程必须使用相同的变量值(即,所有线程都引用变量的相同内存位置)。与原语一起使用时,这意味着您不需要同步(尽管使用64位原语(其中float不是一个),您可能不需要同步,具体取决于您的JVM是32位还是64位)
您可能要观看部分/不一致的更新-当某些sh
变量在另一个线程读取它们时进行更新。您可能希望通过对多个sh变量“ atomic”进行更新来同步更新以保持一致的状态。