【问题标题】:Single static field in Java classJava类中的单个静态字段
【发布时间】:2018-08-27 06:51:44
【问题描述】:

有一个类 RedisLogger.java 用来处理 redis 的 logger。在 RedisLogger.java 中,我使用以下代码声明了一个静态 JedisPool 字段 jedisPool

private static JedisPool jedisPool;

因为 JedisPool 是线程安全的类,我想使用以下代码在我的应用程序中只实例化一次 jedisPool

public static JedisPool getJedisPool() {
    if(jedisPool == null) {
        synchronized (JedisPool.class) {
            if(jedisPool == null) {
                jedisPool = new JedisPool();
            }
        }
    }
    return jedisPool;
}

我用这段代码来测试它。

ExecutorService executor = Executors.newCachedThreadPool();
    for(int i = 0; i < 1000; i++) {
        executor.execute(()->{
            System.out.println(RedisLogger.getJedisPool());
        });
    }

从输出看来效果不错:

redis.clients.jedis.JedisPool@3d11fc5d
redis.clients.jedis.JedisPool@3d11fc5d
redis.clients.jedis.JedisPool@3d11fc5d
redis.clients.jedis.JedisPool@3d11fc5d
redis.clients.jedis.JedisPool@3d11fc5d
redis.clients.jedis.JedisPool@3d11fc5d
redis.clients.jedis.JedisPool@3d11fc5d
redis.clients.jedis.JedisPool@3d11fc5d
redis.clients.jedis.JedisPool@3d11fc5d
redis.clients.jedis.JedisPool@3d11fc5d
redis.clients.jedis.JedisPool@3d11fc5d
redis.clients.jedis.JedisPool@3d11fc5d
redis.clients.jedis.JedisPool@3d11fc5d
....

但它真的能达到我的期望吗? 因为我的申请中有很多这样的地方。例如。

private static Cluster getCluster() {
    if(cluster == null) {
        synchronized (Cluster.class) {
            if(cluster == null) {
                Builder builder = Cluster.builder();
                for (int i = 0; i < MSConfig.SRCDOC_CASSANDRA_ADDRS().length; i++) {
                    builder.addContactPoint(MSConfig.SRCDOC_CASSANDRA_ADDRS()[i])
                            .withPort(MSConfig.SRCDOC_CASSANDRA_PORT()[i]);
                }
                cluster = builder.withCredentials(MSConfig.SRCDOC_CASSANDRA_USERNMAE(), MSConfig.SRCDOC_CASSANDRA_PASSWORD())
                        .withProtocolVersion(ProtocolVersion.V4)
                        .withPoolingOptions(getPoolingOptions())
                        .withSocketOptions(getSocketOptions())
                        .withRetryPolicy(getRetryPolicy())
                        .withQueryOptions(getQueryOptions())
                        .build();
            }
        }
    }
    return cluster;
}

谢谢!!!

【问题讨论】:

  • 但真的能达到我的预期吗。它应该。为什么不应该呢?你看到不一致了吗?我们不知道您的代码,因此我们无法通过查看您的最后一个 sn-p 来判断它是否有效。我们需要全貌,但这会打破这个问题的框架

标签: java multithreading static synchronized


【解决方案1】:

您所做的称为“双重检查锁定”。如果您在 Stackoverflow 或 Google 上搜索此内容,您会发现很多解释为什么它在 Java 中无法正常工作

替代方案是:

  1. 如果在加载它的持有类时你很可能会使用这个对象,那么直接初始化它并使该字段成为 final。
  2. 声明字段volatile
  3. 使用持有类,即有一个私有内部类来持有该字段,然后将其设为final

另请参阅以下问题:

【讨论】:

  • 我知道,双重检查用于单例模式。但我想把它用在一个领域。
  • 您当前拥有的代码可能会泄漏JedisPool 的未初始化实例。这与单例无关。
【解决方案2】:

我想到的唯一补充是放弃 synchronized 初始化,直接使用 static-holder-pattern。来自top answer

JVM延迟初始化 InstanceHolder 类,直到它被实际使用,并且因为 Singleton 是使用静态初始化器初始化的,所以 不需要额外的同步

这可以像这样在你的代码中使用:

public class RedisLogger{
     public static JedisPool getJedisPool(){
         return JedisPoolHolder.INSTANCE;
     }

     private static final class JedisPoolHolder{
         private static final JedisPool INSTANCE = new JedisPool();
     }

     // the rest of your code
}

【讨论】:

    【解决方案3】:

    您可以直接为变量赋值并在您的情况下将其声明为final static

    【讨论】:

    • static 是使某物成为单例的关键字。 final 只保证变量的值不能改变。因此,您的答案是错误的
    • 对,它不一定是关于单例的,但在这种情况下,final 会很好,因为我们不想修改值,所以由于它将在声明期间分配,它可以是 final。虽然编辑了我的答案。
    • 但是你仍然需要static
    • 小误会。我的意思是在修饰符中添加 final,而不是用 final 替换它们。感谢您的编辑。
    • 既然是游泳池,我想它会在某个时候修改......我不认为final在这里
    猜你喜欢
    • 2011-06-21
    • 1970-01-01
    • 2017-12-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多