Подтвердить что ты не робот

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

Спасибо,

4b9b3361

Ответ 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 доступ к безопасности.