Java разделяет переменную между двумя потоками - PullRequest
17 голосов
/ 16 августа 2010

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

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

Спасибо,

Ответы [ 4 ]

17 голосов
/ 16 августа 2010

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

9 голосов
/ 16 августа 2010

В этом случае я бы использовал 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();
        }
    }
}
8 голосов
/ 16 августа 2010

Мало того, что variable должно быть volatile, но вы также хотите защитить свою update функцию с помощью некоторой синхронизации , поскольку ++variable не является атомарным вызовом.В конце концов, это просто синтаксический сахар для

variable = variable + 1;

, который не является атомарным.

Вы должны также обернуть все вызовы, которые читают переменную, в lock некоторогоsort.

В качестве альтернативы используйте 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 голосов
/ 16 августа 2010

Используйте AtomicInteger или synchronize для безопасного доступа.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...