Оператор 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();
}
}
}
, то только один поток возьмет на себя ответственность за отправку сигнала остановки.
Вот пример из проекта, я в настоящее время продолжаю работать. Я подозреваю, что можно изменить его для работы на ситуацию.
<path id="master-classpath">
<fileset dir="${web.dir}/WEB-INF/lib">
<include name="*.jar"/>
</fileset>
<fileset dir="${appserver.lib}">
<include name="servlet*.jar"/>
</fileset>
<pathelement path="${build.dir}"/>
</path>
...
<javac destdir="${build.dir}">
<src path="${src.dir}"/>
<classpath refid="master-classpath"/>
</javac>
Попробовать
<javac ... classpathref="TSA.classpath">
или
<javac ...>
...
<classpath refid="TSA.classpath" />
</javac>
Попробуйте это:
<classpath refid="TSA.classpath"/>