Java разделяет переменную между двумя потоками

У меня два потока. Один вызывает метод обновления класса, который изменяет переменную. Другой вызывает метод обновления класса, который читает переменную. Только один поток записывает и один (или несколько) потоков читает эту переменную. Что мне нужно сделать с точки зрения параллелизма, поскольку я новичок в многопоточности?

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...
    }
}

Спасибо,

17
задан Dave 16 August 2010 в 14:11
поделиться

4 ответа

Если есть один и только один поток, который записывает в переменную , вы можете обойтись, сделав его изменчивым . В противном случае см. Ответ с помощью AtomicInteger .

Только volatile будет работать в случае только одного потока записи, потому что существует только один поток записи, поэтому он всегда имеет правильное значение переменной .

17
ответ дан 30 November 2019 в 11:37
поделиться

Не только переменная должна быть изменчивой , но вы также хотите защитить свою функцию 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...
    }
}

Для более сложных алгоритмов, как предполагает ваша последняя редакция, используйте синхронизацию или блокировки.

8
ответ дан 30 November 2019 в 11:37
поделиться

В этом случае я бы использовал 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();
        }
    }
}
9
ответ дан 30 November 2019 в 11:37
поделиться

Использование AtomicInteger или synchronize ] доступ, чтобы быть в безопасности.

4
ответ дан 30 November 2019 в 11:37
поделиться