Являются ли примитивные типы данных поточно-ориентированными в Java
Являются ли примитивные типы данных, как 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
значение.
Найти похожий пример здесь
3 ответа
Есть три способа, которыми они небезопасны:
long
а такжеdouble
даже не гарантируется, что они обновляются атомарно (вы могли видеть половину записи из другого потока)- Модель памяти не гарантирует, что вы увидите последние обновления из одного потока в другом потоке, без каких-либо дополнительных барьеров памяти
- Акт приращения переменной в любом случае не атомарен
использование AtomicInteger
и т. д. для поточно-ориентированных операций.
Примитивные типы не являются потокобезопасными. Проверьте этот учебник.
Я бы предложил использовать классы в java.util.concurrent.atomic. Они разработаны для обеспечения безопасности потоков, а в некоторых случаях JVM может использовать преимущества аппаратных функций для оптимизации.
Для чтения / записи значения в многопоточной среде программа должна иметь надлежащую синхронизацию или блокировку, чтобы предотвратить скачки данных. Это не имеет никакого отношения к тому, к какому типу данных получить доступ. В идеальном мире мы не должны делиться ничем или делиться только неизменяемыми объектами, что всегда является потокобезопасным.
Теоретически, согласно https://docs.oracle.com/javase/specs/jls/se8/html/jls-17.html, ОДНАКО, реализация имеет тенденцию к атомарности, следующий код ничего не распечатывает с ключевым словом volatile или без него в моей среде (64-битный ubuntu 18.04, 64-битный процессор Intel, Oracle JDK 8), поэтому он является атомарным в этой ситуации, что, я думаю, применимо ко всем Intel/AMD 64 ЦПУ. Мы могли бы сделать то же самое и для 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);
}
}