像
int
和 short
这样的基本数据类型在 Java 中是线程安全的吗?我执行了以下代码,有时看不到预期结果 500。
public class SampleThree extends Thread
{
static long wakeUpTime = System.currentTimeMillis() + (1000*20);
static int inT;
public static void main(String args[])
{
System.out.println("initial:" + inT);
for(int i=0; i<500; i++)
new SampleThree().start();
try {
Thread.sleep(wakeUpTime - System.currentTimeMillis() + (1000*30));
System.out.println("o/p:" + inT);
}
catch(Exception e){
e.printStackTrace();
}
}
public void run()
{
try {
long s = wakeUpTime - System.currentTimeMillis();
System.out.println("will sleep ms: " + s);
Thread.sleep(s);
inT++; // System.out.println(inT);
}
catch(Exception e) {
e.printStackTrace();
}
}
}
这里并发500个线程会更新int变量
inT
。主线程等待并发更新完成后,打印inT
值。
查找类似示例此处
它们在三种情况下是不安全的:
long
和 double
甚至不能保证原子更新(您可以看到来自不同线程的一半写入)AtomicInteger
等进行线程安全操作。
原始类型不是线程安全的。检查这个教程。
我建议使用 java.util.concurrent.atomic 中的类。它们是为线程安全而设计的,在某些情况下,JVM 可以利用硬件功能进行优化。
要在多线程环境中读取/写入值,程序应该具有适当的同步或锁定以防止数据竞争。它与访问哪种数据类型无关。在理想的世界中,我们应该不共享任何内容或只共享不可变对象,这始终是线程安全的。
理论上,根据https://docs.oracle.com/javase/specs/jls/se8/html/jls-17.html#jls-17.7,甚至不能确保long/double是原子的 然而,实现趋于原子化,下面的代码在我的环境(64位ubuntu 18.04,Intel 64位CPU,Oracle JDK 8)中打印出任何带或不带易失性关键字的内容,所以在这种情况下它是原子的,我想这适用于所有英特尔/AMD 64 CPU。 我们也可以对 double 做同样的事情,尽管构造带有某些属性来检查的 double 值有点棘手。
public class LongThreadSafe {
// multiple threads read and write this value.
// according to the java spec, only volatile long is guaranteed to be atomic
private static long value = 0;
private static final int max = (1 << 30) - 1;
private static final int threadCount = 4;
static ExecutorService executorService = Executors.newFixedThreadPool(threadCount);
static CyclicBarrier barrier = new CyclicBarrier(threadCount);
public static void main(String[] args) throws InterruptedException {
for (int i = 0; i < threadCount; i++) {
executorService.submit(() -> {
try {
// all threads start to work at the same time
barrier.await();
} catch (Exception e) {
e.printStackTrace();
}
for (int j = 1; j < max; j++) {
// read value into v2
long v2 = value;
// check v2
int low = (int) v2;
int high = (int) (v2 >> 32);
if ((high << 1) != low) {
System.out.println("invalid number found high=" + high + ", low=" + low);
}
// write LongThreadSafe.value again
LongThreadSafe.value = ((long) j << 32) | (long) (j << 1);
}
});
}
executorService.shutdown();
executorService.awaitTermination(10, TimeUnit.MINUTES);
}
}