为什么在安卓中lockStatic是volatile的?

3
我正在尝试理解这个Android实现,但我无法想象为什么静态字段“lockStatic”要是易失性的,因为它仅通过“getLock”方法使用并且是同步块,所以不应该存在并发或对象发布问题。
abstract public class WakefulIntentService extends IntentService {
    abstract protected void doWakefulWork(Intent intent);
    static final String NAME=
      "com.commonsware.cwac.wakeful.WakefulIntentService";
    static final String LAST_ALARM="lastAlarm";

    private static volatile PowerManager.WakeLock lockStatic=null;

    synchronized private static PowerManager.WakeLock getLock(Context context) {
       if (lockStatic == null) {
           PowerManager mgr = (PowerManager)context.getSystemService(Context.POWER_SERVICE);

           lockStatic=mgr.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, NAME);
           lockStatic.setReferenceCounted(true);
       }

       return(lockStatic);
    }

我认为只有当lockStatic可以以其他方式访问时,才需要使用volatile关键字。但是它是私有的,并且唯一处理它的方法是getLock方法。这只是一个断言吗?还是有其他原因?感谢您提供任何帮助以解决此问题。
请参考WeakfullIntentService

4
我很确定我是支持双重检查锁定实现的那种方式完成的。然而,总的来说,在Stack Overflow上问“为什么开发者X做了Y事情?”并不是一个好问题。唯一能给出明确答案的人是开发者X本人。在这种情况下,我就是开发者X,我几年前写了那段代码。没有其他人可以给出明确的答案。 - CommonsWare
2
@CommonsWare虽然出现并解释主要框架代码的作者很酷。 - chrylis -cautiouslyoptimistic-
1个回答

2
在代码示例中,不需要使用volatile,因为变量仅在该synchronized块中访问。
然而,synchronized可能有点昂贵,特别是考虑到大多数调用该方法只是读取变量并返回。在每次读取时都用synchronized感觉太重了。因此,我们有所谓的双重检查模式,它大部分时间都避免了synchronized
if(var==null)
    synchronized(lock)
        if(var==null)         // double-check
            var = something
return var

(OP的代码示例实际上不是双重检查锁定,而是一直锁定。)
在双重检查锁定中,通常应该将 "var" 设为 "volatile"--但并非总是如此。如果分配给它的对象是原始类型 (比如 int) 或不可变类型 (比如 String),则非 volatile 是可以的。
还有更多情况下可以使用非 volatile。这需要仔细评估使用情况。例如,java.util.concurrent 中的所有实用工具类 (如 ReentrantLock) 都是以一种方式设计的,使它们能够经受住 "不安全发布"。当它们在双重检查锁定模式中使用时,它们不必是 volatile。看到 WakeLock 的源代码,我相信它在双重检查锁定中也不需要 volatile。
当然,这有点过于复杂了。因此,除非您确切知道自己在做什么,请谨慎使用 volatile。与普通读取相比,volatile 读取并不会增加太多开销。

1
请注意,“volatile”关键字在所有版本的Android上(特别是旧版Dalvik VM实现)并不提供内存可见性保证,因为Android不遵循Java内存模型。我曾经因此而受到过伤害,因为VM基本上忽略了volatile关键字,如果您需要在Android上使用volatile语义,请使用“AtomicReference”或其他java.util.concurrent AtomicXXX类。 - GameSalutes
@GameSalutes - 真糟糕:) 我本以为谷歌会比Sun做得更好,毕竟它有如此多的资金和人才。 - ZhongYu
确实。至少原子类提供了实现者必须遵守的约定,我已经在Android上成功地使用它们,并且没有使用volatile关键字。这里有一个讨论它的线程:https://dev59.com/WGw05IYBdhLWcg3w3lol - GameSalutes

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接