跨多个线程访问String

时间:2014-07-18 18:34:33

标签: java multithreading synchronization

我在这里寻找一些意见。我有一个单例类,其中包含一个值,该值每隔几秒由该类中的方法更新。现在,跨多个线程访问此值是通过同步完成的,我想消除。这有意义吗?

class DataSegment {

    private MetricsUpdater metrics = new MetricsUpdater.getInstance();

    public String printValues() {
        StringBuilder sb = new StringBuilder();
        sb.append(value1);
        sb.append(morevalues);
        sb.append(metrics.myValue); // this is the value that's currently synchronized
        return sb.toString();
    }
}


class MetricsUpdater {

    private String myValueSynchronized;
    public String myValue;

    public static MetricsUpdater getInstance() {
        if (theInstance == null) {
            theInstance = new MetricsUpdater();
        }
        return theInstance;
    }

    // this runs on a timer but to keep it simple I'll just define the method...
    private void updateMetrics() {

        synchronized(myValue) {
            // also, to keep things simple, I've replaced all of the actual logic with a method called someMethodToUpdateMyValue()
            myValueSynchronized = someMethodToUpdateMyValue();
            myValue = myValueSynchronized;
        }
    }
}

可能有很多DataSegment实例都是从myValue中读取的,但metrics类是一个单例。 myValue仅每5秒更新一次,并且只允许MetricsUpdater写入。这有意义吗?

如果只允许所有其他线程读取它,它是否甚至需要同步?我已经对此运行了大量的JUnit测试,创建了DataSegment类的许多实例,所有打印值都像疯了一样,我还没有看到任何并发问题。

3 个答案:

答案 0 :(得分:4)

您的代码存在一些问题。

第一个问题

synchronized(myValue) {
    myValueSynchronized = someMethodToUpdateMyValue();
    myValue = myValueSynchronized;
    Thread.sleep(100); 
}

您的关键部分错误,因为锁定myValue 。假设您在退出临界区之前放置了Thread.sleep(100)。然后它意味着其他线程将锁定新的myValue实例,从而可以进入临界区。如果它是一个时间线程,如果它的频率非常高。然后你可以更新陈旧覆盖新的。 无论如何,这是一个不好的实践,以锁定此类显示器。使用ReentrantLock或同步String的最终引用。

第二个问题

    public static MetricsUpdater getInstance() {
        if (theInstance == null) {
            theInstance = new MetricsUpdater();
        }
        return theInstance;
    }

您的Singleton代码已损坏。使用DCL(双重检查锁定在我的解决方案中见下文)。 或者使用私有静态MetricsUpdater theInstance = new MetricsUpdate();.后者更好,

第3个问题

 sb.append(metrics.myValue); 

上述代码应在同步上下文中调用或声明为volatile。后者更好

解决方案1 ​​ - 假设someMethodToUpdateMyValue是线程安全的

class MetricsUpdater {

    private static volatile MetricsUpdater theInstance;
    public volatile String myValue;

    /**
     * DCL . Please avoid
     * Better use 
     * private static MetricsUpdater theInstance = new MetricsUpdate();
     */
    public static MetricsUpdater getInstance() {
        if (theInstance == null) {
            synchronized(MetricsUpdate.class) {
                 if(theInstance == null) {
                     theInstance = new MetricsUpdater();
                 }
            }
        }
        return theInstance;
    }

    // this runs on a timer but to keep it simple I'll just define the method...
    // if your someMethodToUpdateMyValue is thread safe
    private void updateMetrics() {
            myValue = someMethodToUpdateMyValue();
    }
}

解决方案2:假设someMethodToUpdateMyValue不是线程安全

不需要同步,引用读/写是原子的 我们将myValue称为易失性

class MetricsUpdater {

 private static volatile MetricsUpdater theInstance;
 public volatile String myValue;

 /**
 ** Use ReentrantLock instead
 */
 private final  Object lock  = new Object();


     /**
     * DCL . Please avoid
     * Better use 
     * private static MetricsUpdater theInstance = new MetricsUpdate();
     */
    public static MetricsUpdater getInstance() {
        if (theInstance == null) {
            synchronized(MetricsUpdate.class) {
                 if(theInstance == null) {
                     theInstance = new MetricsUpdater();
                 }
            }
        }
        return theInstance;
    }

// this runs on a timer but to keep it simple I'll just define the method...
private void updateMetrics() {
    synchronized(lock) {
        myValue = someMethodToUpdateMyValue();
    }
}

}

答案 1 :(得分:3)

它确实需要同步,或者多个线程读取的变量需要标记为volatile(或任何导致java刷新变量值的其他内容)。 java内存模型不保证一个线程(永远)会看到另一个线程写入的变量的值。实际上,这些值通常可以被多个线程正确看到,但如果要确保它,则必须正确同步(或使用volatile / locks / etc)以确保刷新值。

答案 2 :(得分:3)

是的,必须在同一个锁上的同步块中读取myValue以查看myValue的最新值。

所以你可以附上myValue:

synchronized (metrics)
{
     sb.append(metrics.myValue); // this is the value that's currently synchronized
}

并改为:

synchronized(this) {
    // also, to keep things simple, I've replaced all of the actual logic with a method called someMethodToUpdateMyValue()
    myValueSynchronized = someMethodToUpdateMyValue();
    myValue = myValueSynchronized;
}

我也没有必要从myValueSynchronized看到。只要保持myValue的值与对象中的其余数据保持一致,就可以使用myValue。