У меня два потока. Один вызывает метод обновления класса, который изменяет переменную. Другой вызывает метод обновления класса, который читает переменную. Только один поток записывает и один (или несколько) потоков читает эту переменную. Что мне нужно сделать с точки зрения параллелизма, поскольку я новичок в многопоточности?
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...
}
}
Спасибо,
Если есть один и только один поток, который записывает в переменную
, вы можете обойтись, сделав его изменчивым
. В противном случае см. Ответ с помощью AtomicInteger
.
Только volatile
будет работать в случае только одного потока записи, потому что существует только один поток записи, поэтому он всегда имеет правильное значение переменной
.
Не только переменная
должна быть изменчивой
, но вы также хотите защитить свою функцию update
с помощью какой-то синхронизации , поскольку ++ переменная
не является атомарным вызовом. В конце концов, это всего лишь синтаксический сахар для
variable = variable + 1;
, который не является атомарным.
Вам также следует заключить любые вызовы, которые читают переменную, в какую-либо блокировку .
В качестве альтернативы используйте 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...
}
}
Для более сложных алгоритмов, как предполагает ваша последняя редакция, используйте синхронизацию или блокировки.
В этом случае я бы использовал AtomicInteger , однако общий ответ заключается в том, что доступ к переменной должен быть защищен синхронизированным блоком или с помощью другой части java. .util.concurrent пакет.
Пара примеров:
Использование synchronized
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();
}
}
}
Использование AtomicInteger
или synchronize
] доступ, чтобы быть в безопасности.