经常访问的变量会从主存读取到线程的高速缓冲区,导致不同线程间对数据的修改不能及时同步:
import java.util.concurrent.TimeUnit;
class TObject{
public boolean b = false;
}
public class VisibilityTest {
static boolean flag = true;
static int num = 1;
static String s = "a";
static TObject tObject = new TObject();
static Object object;
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(() -> {
while (flag) {
if (num==100) break;
if (s.equals("b")) break;
if (tObject.b) break;
if (object!=null) break;
// System.out.println("this can stop!");
}
});
t1.start();
TimeUnit.MILLISECONDS.sleep(10);
flag = false;
num = 100;
s = "b";
tObject.b = true;
object=new Object();
}
}
上方代码直接运行,t1线程始终无法读到main线程对值的修改正常结束。
要想同步t1和main的值。
- 可以在变量前加volatile(易变的)关键字,使得每个线程对变量的访问不生成高速缓存,必须去主存读取和修改(保证了可见性但降低了性能)。
- 遇到同步代码块或者同步方法(synchronized),也会更新线程私有高速缓冲内存的值,println是一个同步方法,上方注释//System.out.println("this can stop!");打开会导致更新缓冲区的值。
- 若上方TimeUnit.MILLISECONDS.sleep(10); 修改为TimeUnit.MILLISECONDS.sleep(1);也会成功停止,因为高速缓冲还未形成,t1还是从主存中去读取的变量值。
注:
volatile和synchronized都可以实现线程间的可见性,但volatile没有上锁步骤,在实现可见性方面更轻量,但不能保证原子性。
网友评论