首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >Xlet开发中可能出现的并发问题

Xlet开发中可能出现的并发问题
EN

Stack Overflow用户
提问于 2012-10-17 20:39:27
回答 1查看 165关注 0票数 1

我参与了使用Java1.4API开发Xlet

文档说Xlet接口方法(实际上是xlet生命周期方法)是在它的特殊线程(不是EDT线程)上调用的。我通过日志检查了一下--这是真的。这对我来说有点令人惊讶,因为它与在EDT上调用生命周期方法的BB/Android框架不同,但到目前为止还可以。

在项目代码中,我看到应用程序广泛使用Display.getInstance().callSerially(Runnable task)调用(这是在EDT线程上运行Runnable的一种LWUIT方式)。

因此,基本上Xlet实现类中的一些代码在EDT线程的xlet内部状态对象上执行创建/更新/读取操作,而生命周期线程中的其他一些代码在没有任何同步的情况下执行创建/更新/读取操作(包括状态变量没有声明为易失性)。像这样的Smth:

代码语言:javascript
复制
class MyXlet implements Xlet {

    Map state = new HashMap();

    public void initXlet(XletContext context) throws XletStateChangeException {
        state.put("foo", "bar"); // does not run on the EDT thread

        Display.getInstance().callSerially(new Runnable() {
            public void run() {
                // runs on the EDT thread
                Object foo = state.get("foo");
                // branch logic depending on the got foo
            }
        });
    }

    ..
}

我的问题是:这是否为罕见的并发问题创造了背景?对状态的访问是否应该显式同步(或者至少应该将状态声明为易失性)?

我的猜测是,这取决于代码是否在多核CPU上运行,因为我知道,在多核CPU上,如果有两个线程在自己的内核上运行,那么变量就会被缓存,因此每个线程都有自己的状态版本,除非显式同步。

我希望得到一些关于我所关心的问题的可信的回应。

EN

回答 1

Stack Overflow用户

发布于 2012-11-05 23:42:05

是的,在您描述的场景中,对共享状态的访问必须是线程安全的。

有两个问题需要注意:

第一个问题,可视性(您已经提到过),在单处理器上仍然可能发生。问题是允许JIT编译器在寄存器中缓存变量,在上下文切换时,操作系统很可能会将寄存器的内容转储到线程上下文中,以便稍后可以恢复。然而,这与将寄存器的内容写回对象的字段不同,因此在上下文切换之后,我们不能假设对象的字段是最新的。

例如,使用以下代码:

代码语言:javascript
复制
class Example {
    private int i;

    public void doSomething() {
        for (i = 0; i < 1000000; i ++) {
            doSomeOperation(i);
        }
    }
}

由于循环变量(实例字段) CPU没有声明为易失性,因此允许i使用CPU寄存器优化循环变量i。如果发生这种情况,则在循环完成之前,JIT将不需要将寄存器的值写回实例变量i

所以,假设一个线程正在执行上面的循环,然后它被抢占了。新调度的线程将无法看到i的最新值,因为i的最新值在一个寄存器中,并且该寄存器被保存到线程本地执行上下文中。至少需要将实例字段i声明为volatile,以强制i的每次更新对其他线程可见。

第二个问题是对象状态的一致性。以代码中的HashMap为例,在内部,它由几个非最终成员变量sizetablethresholdmodCount组成。其中table是形成链表的Entry数组。当将元素放入映射中或从映射中删除元素时,需要自动更新这些状态变量中的两个或更多,以使状态保持一致。对于HashMap,这必须在synchronized块或类似的块中完成,才能使其成为原子的。

对于第二个问题,在单处理器上运行时仍然会遇到问题。这是因为操作系统或虚拟机可以在当前线程执行put或remove方法的过程中抢先切换线程,然后切换到尝试在同一HashMap上执行其他操作的另一个线程。

想象一下,如果您的EDT线程正在调用' get‘方法时发生抢占式线程切换,并且您得到一个试图向map中插入另一个条目的回调,会发生什么情况。但这一次,映射超过了负载因子,导致映射调整大小,所有条目都被重新散列并插入。

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/12934511

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档