Java разделяет переменную между двумя потоками
У меня есть два потока. Один вызывает метод обновления класса, который изменяет переменную. Другой вызывает метод обновления класса, который читает переменную. Только один поток пишет и один (или более) поток читает эту переменную. Что мне нужно сделать с точки зрения concurrency, так как я новичок в многопоточности?
public class A
{
public int variable; // Does this need to be volatile?
// Not only int, could also be boolean or float.
public void update()
{
// Called by one thread constantly
++variable;
// Or some other algorithm
variable = complexAlgorithm();
}
}
public class B
{
public A a;
public void update()
{
// Called by another thread constantly
// I don't care about missing an update
int v = a.variable;
// Do algorithm with v...
}
}
Спасибо,
Ответы
Ответ 1
Если есть один и только один поток, который записывается в variable
, вы можете уйти с созданием volatile
. В противном случае см. Ответ AtomicInteger
.
Только volatile
будет работать только в одном потоке записи, потому что есть только один поток записи, поэтому он всегда имеет правильное значение variable
.
Ответ 2
В этом случае я бы использовал AtomicInteger, однако обобщенный ответ заключается в том, что доступ к переменной должен быть защищен синхронизированным блоком, или с помощью другой части пакета java.util.concurrent.
Несколько примеров:
Использование синхронизированных
public class A {
public final Object variable;
public void update() {
synchronized(variable) {
variable.complexAlgorithm();
}
}
}
public class B {
public A a;
public void update() {
sychronized(a.variable) {
consume(a.variable);
}
}
}
Использование java.util.concurrent
public class A {
public final ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
public final Object variable;
public void update() {
lock.writeLock().lock();
try {
variable.complexAlgorithm();
} finally {
lock.writeLock().unlock();
}
}
}
public class B {
public A a;
public void update() {
a.lock.readLock().lock();
try {
consume(a.variable);
} finally {
a.lock.readLock().unlock();
}
}
}
Ответ 3
Не только variable
be volatile
, но вы также хотите защитить свою функцию update
некоторая синхронизация поскольку ++variable
не является атомарным вызовом. Это, в конце концов, просто синтаксический сахар для
variable = variable + 1;
который не является атомарным.
Вы также должны обернуть любые вызовы, которые читают переменную в lock.
В качестве альтернативы используйте AtomicInteger. Это было сделано для такого рода вещей (только для целочисленных операций).
public class A
{
// initially had said volatile wouldn't affect this variable because
// it is not a primitive, but see correction in comments
public final AtomicInteger variable; // see comments on this issue of why final
public void update()
{
// Called by one thread constantly
variable.getAndIncrement(); // atomically adds one
}
public int retrieveValue()
{
return variable.get(); // gets the current int value safely
}
}
public class B
{
public A a;
public void update()
{
// Called by another thread constantly
int v = a.retrieveValue();
// Do algorithm with v...
}
}
Для более сложных алгоритмов, как предполагает ваше недавнее изменение, используйте синхронизацию или блокировки.
Ответ 4
Используйте AtomicInteger
или synchronize
доступ к безопасности.