Java volatile引用是在线程之间传递MotionEvents的安全方法吗?

Java volatile引用是在线程之间传递MotionEvents的安全方法吗?,java,android,multithreading,Java,Android,Multithreading,我对这种方法的安全性感到好奇,我曾考虑在Android应用程序中使用这种方法来传递触摸事件(并测试我对Java并发性的理解)。以下是基本知识: 我有一个连接到SurfaceHolder.Callback的SurfaceView来获取用户输入事件,主要是onTouchEvent回调。调用onTouchEvent方法后,我会查看event.getAction()==MotionEvent.ACTION\u UP,如果是,则调用我命名的postTouchEvent方法,它是我的应用程序线程的成员方法,

我对这种方法的安全性感到好奇,我曾考虑在Android应用程序中使用这种方法来传递触摸事件(并测试我对Java并发性的理解)。以下是基本知识:

我有一个连接到SurfaceHolder.Callback的SurfaceView来获取用户输入事件,主要是
onTouchEvent
回调。调用
onTouchEvent
方法后,我会查看
event.getAction()==MotionEvent.ACTION\u UP
,如果是,则调用我命名的
postTouchEvent
方法,它是我的应用程序线程的成员方法,用于更新应用程序的状态并绘制到画布

表面观察

@Override
public boolean onTouchEvent(final MotionEvent event)
{
    mAppThread.postTouchEvent(event);
}
应用线程

private volatile MotionEvent mInputEvent;

public void postTouchEvent(final MotionEvent event)
{
    this.mInputEvent = event;
}

@Override
public void run()
{
    // while loop ...

    if (this.mInputEvent != null)
    {
        final MotionEvent inputEvent = this.mInputEvent;
        this.mInputEvent == null;

        // update state based on inputEvent (not this.mInputEvent)
    }

    // draw to canvas
}
现在我明白了它肯定不是原子的,但既然我在从框架接收到它后将其视为不可变的,这难道不起作用吗?(而不是同步post方法和if语句,我没有问题,但我要求学习。)

这是我的想法。我知道我将有一个对该对象的有效引用,但我不确定在什么状态下我将实际看到该对象。虽然测试一切正常,但我知道线程异常是多么罕见,即使有些东西坏了

此外,我还发现了一个问题:如果出现另一个MotionEvent,则
run()
方法中的inputEvent可能会被设置为与
this.mInputEvent!=已选中null,但这实际上不是问题

那么,我有什么遗漏吗?或者,就我的目的而言,这应该是正确的吗?

我认为这是“安全的”,将您已经确定的关于丢失事件的点模化

volatile上的写入操作和后续读取操作之间存在“发生在”关系。这与单个线程中动作固有的“发生在”关系相结合,意味着您的应用程序线程将看到事件对象在其引用写入volatile时的真实状态


当然,如果事件侦听器线程在更新引用后更改事件对象的状态,则无法保证应用程序线程会看到它们。同上,如果第三个线程更新事件对象。

否,这是不安全的,但不是出于您可能期望的原因

。这是将MotionEvents发送到视图层次结构的代码。第1841行是
finally
块的一部分,该块调用刚刚调度的MotionEvent

MotionEvents不像大多数对象那样被垃圾收集,它们被池化和回收,以避免事件分派期间不必要的内存分配和垃圾收集。循环使用MotionEvent会将其返回到对象池,以便在以后需要新的MotionEvent时再次使用。调用
recycle()
后,应认为MotionEvent无效

您的示例代码可能最终读取一个MotionEvent对象,该对象已被框架重用,现在包含完全不同的数据

如果您计划在
onTouchEvent
返回后挂起MotionEvent,请使用克隆。static
get()
方法将从对象池返回具有相同内容的新MotionEvent


处理完动作事件后,应调用
recycle()
,然后
get()
将其返回到池中。如果您忘记了这一步,这并不是什么大问题,它将成为常规Java垃圾,框架将在需要时创建新的MotionEvents。但是,系统可以非常快速地调度MotionEvents,在这种情况下,配合此优化可以对某些设备的性能产生重大影响。

这正是我想要了解的。感谢您抽出时间提供如此详细的答案和建议的解决方案。