访问Android服务的实例变量时何时需要同步?

时间:2013-12-28 04:48:15

标签: android multithreading android-service race-condition

换句话说,根据我的理解,这不是线程安全的(对于startService()的多次调用):

public class RaceService extends Service {

    volatile int a; // edit: added volatile to clarify my point

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        new Thread(new Runnable() {

            @Override
            public void run() {
                a++;
                Log.w("RaceService", "a: " + a);
            }
        }).start();
        return super.onStartCommand(intent, flags, startId);
    }
}

因为只创建了一个服务实例(尽管从未设法为此找到明确的断言 - 如果有人能指出我实际实例化服务的源(通过反射?),我将不胜感激)。如果每个startService()创建一个实例,则只需在每次调用startService时打印1。

但是这是:

public class RaceService2 extends Service {

    int a;

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        a++;
        Log.w("RaceService2", "a: " + a);
        return super.onStartCommand(intent, flags, startId);
    }
}

线程安全(多次调用startService())?

使用IntentService会对这两个示例(代码位于onHandleIntent()中)产生影响吗?

1 个答案:

答案 0 :(得分:1)

它与Java或Android中其他任何地方的同步需求相同 - 如果您有多个线程访问变量,您可能需要同步方法来防止线程干扰,或者您可能需要将变量声明为volatile,以便每个线程获得最新的副本。

Android特别需要注意的是,onStartCommand将由主线程上的系统调用,因此除非您另外执行,否则您将阻止主UI线程。如果使用IntentService,则在另一个线程上调用onHandleIntent方法,因此您可以在那里卸载长时间运行的任务,而无需担心创建和使用单独线程的机制。

您可以使用易变实例变量的地方是在onHandleIntent中改变变量,然后在onDestroy中访问它。在这里,您希望确保在调用onDestroy时获得一份新的副本。

你的例子可以说的是,在你的第一个例子中,Log语句中a的值是未定义的,而在第二个例子中,如果这是一个新对象,则它是1。

编辑:最重要的是,如果你多次调用startService(),你真的不应该指望实例变量的值是什么(我们不知道它是否会成为同一个实例它可能已经被破坏了)你不应该依赖于对startService()的调用顺序必须与它们执行的顺序相同(即使这似乎是它的实现方式)。

将请求填入数组并将它们作为一个调用发送到startService(),或者如果需要这样的保证,则使用服务中的局部变量。

如果你在服务中使用多线程这是一个更复杂的场景,你应该发布一个问题的具体例子,因为不知道你在尝试什么,就不可能给出建议。