Оператор stop = true;
не является операцией «чтение-запись», а только запись . Он вообще не читает старое значение переменной. Если предыдущее значение stop
было true
, оператор не имел никакого эффекта, не заметив разницу.
Операция «чтение-изменение-запись», также известная как операция «чтение-обновление-запись», подразумевает операцию, которая считывает предыдущее значение, вычисляет новое значение на его основе и записывает новое значение обратно в переменную. , Проблема с этой операцией, когда не используется специальная атомарная конструкция обновления, состоит в том, что к тому времени, когда выполняется запись, может произойти одновременное обновление, поэтому переменная перезаписывается вычисленным значением, которое основано на устаревшем предыдущем значении .
Для вашей переменной boolean
операции «чтение-изменение-запись» могут выглядеть как
if(!stop) stop = true;
или
stop = !stop;
, но для первого варианта пропуск одновременного обновления не окажет большого влияния, поскольку оператор не имеет эффекта, если переменная уже true
. Второй может пропустить обновления, если они выполняются одновременно, следовательно, не отражают правильное количество операций переворачивания, но использование одновременных обновлений boolean
для более чем одного перехода состояния в целом подвержено ошибкам.
Операция «чтение-запись», т.е. без «изменения / обновления» между ними, будет операцией, которая считывает старое значение для последующего использования и записывает новое значение, не основанное на старом значении. Как
Type old = variable;
variable = newValue;
// use old here
, которые все еще будут подвержены потерянным обновлениям, если не будут сделаны атомарно. Таким образом, такая операция также требует больше, чем volative
переменная. Например. AtomicInteger.getAndSet
или VarHandle.getAndSet
.
Итак, расширив свой пример до
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.LockSupport;
public class StopThread {
private static volatile boolean stop;
public static void main(String[] args) throws InterruptedException {
new Thread(new Runnable() {
@Override
public void run() {
while(!stop) {
System.out.println("In while...");
}
}
}).start();
for(int i = 0; i < 5; i++) {
new Thread(new Runnable() {
@Override
public void run() {
LockSupport.parkNanos(TimeUnit.SECONDS.toNanos(1));
boolean old = stop; // broken because
stop = true; // not atomic
System.out.println(old? "other thread was faster": "sent stop signal");
}
}).start();
}
}
}
несколько потоков могут подумать, что отправили сигнал остановки.
Если вы исправите код
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.locks.LockSupport;
public class StopThread {
private static final AtomicBoolean stop = new AtomicBoolean();
public static void main(String[] args) throws InterruptedException {
new Thread(new Runnable() {
@Override
public void run() {
while(!stop.get()) {
System.out.println("In while...");
}
}
}).start();
for(int i = 0; i < 5; i++) {
new Thread(new Runnable() {
@Override
public void run() {
LockSupport.parkNanos(TimeUnit.SECONDS.toNanos(1));
boolean old = stop.getAndSet(true);
System.out.println(old? "other thread was faster": "sent stop signal");
}
}).start();
}
}
}
именно один поток возьмет на себя ответственность за отправку сигнала остановки.