Многопоточность:Объектам присваивается значение null при их использовании

StackOverflow https://stackoverflow.com/questions/365743

Вопрос

У меня есть небольшое приложение, в котором есть поток рендеринга.Все, что делает этот поток, - это рисует мои объекты в их текущем местоположении.

У меня есть какой-то код, например:

public void render()
{
    // ... rendering various objects

    if (mouseBall != null) mouseBall.draw()

}

Тогда у меня также есть некоторый обработчик мыши, который создает и устанавливает mouseBall в новое положение, когда пользователь щелкает мышью.Затем пользователь может перемещать мышь по кругу, и мяч будет следовать за движением мыши.Когда пользователь отпускает мяч, у меня возникает другое событие мыши, которое устанавливает mouseBall = null.

Проблема в том, что мой цикл рендеринга выполняется достаточно быстро, чтобы в случайные моменты времени условное (mouseBall != null) возвращало true, но через долю секунды после этого пользователь отпустит мышь, и я получу исключение nullpointer для попытки .draw() для нулевого объекта.

Каково решение подобной проблемы?

Это было полезно?

Решение

Проблема заключается в том, что вы получаете доступ mouseBall дважды, один раз, чтобы проверить, не является ли это null и еще один для вызова функции на нем.Вы можете избежать этой проблемы, используя временное решение, подобное этому:

public void render()
{
    // ... rendering various objects
    tmpBall = mouseBall;
    if (tmpBall != null) tmpBall.draw();
}

Другие советы

Вы должны синхронизировать операторы if и draw, чтобы они гарантированно выполнялись как одна атомарная последовательность.В java это было бы сделано следующим образом:

    
public void render()
{
    // ... rendering various objects
    synchronized(this) {
        if (mouseBall != null) mouseBall .draw();
   }
}

Я знаю, что вы уже приняли другие ответы, но третьим вариантом было бы использовать класс AtomicReference пакета java.util.concurrent.atomic .Это обеспечивает операции поиска, обновления и сравнения, которые действуют атомарно, без необходимости какого-либо вспомогательного кода.Итак, в вашем примере:

public void render()
{
    AtomicReference<MouseBallClass> mouseBall = ...;

    // ... rendering various objects
    MouseBall tmpBall = mouseBall.get();
    if (tmpBall != null) tmpBall.draw();
}

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

Следовательно, точный пример, используемый здесь, не очень хорош для демонстрации мощи AtomicReferences.Вместо этого учтите, что ваш другой поток обновит значение mouseball, доступное только в том случае, если оно уже было null - полезная идиома для различных блоков кода в стиле инициализации.В этом случае обычно было бы важно использовать синхронизацию, чтобы гарантировать, что если вы проверите и обнаружите, что значение ball равно null, это будет все еще быть нулевым, когда вы пытались его установить (в противном случае вы возвращаетесь к своей первоначальной проблеме).Однако с помощью AtomicReference вы можете просто сказать:

mouseBall.compareAndSet(null, possibleNewBall);

поскольку это атомарная операция, поэтому, если один поток "увидит" значение как null, он также установит его в ссылку possibleNewBall, прежде чем любые другие потоки получат возможность прочитать его.

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

   MouseBall oldBall = mouseBall.getAndSet(newMouseBall);
   // Cleanup code using oldBall

AtomicIntegers обладают этими преимуществами и многим другим;метод getAndIncrement() замечательно подходит для глобально разделяемых счетчиков, поскольку вы можете гарантировать, что каждый его вызов вернет отдельное значение, независимо от чередования потоков.Безопасность потока с минимумом хлопот.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top