9

考虑下面的代码狙击手:

package sync;

public class LockQuestion {
    private String mutable;

    public synchronized void setMutable(String mutable) {
        this.mutable = mutable;
    }

    public String getMutable() {
        return mutable;
    }   
}

在 Time1 线程 Thread1 将更新“可变”变量。为了将内存从本地缓存刷新到主内存,setter 需要同步。在 Time2 时间(Time2 > Time1,没有线程争用)线程 Thread2 将读取 mutable 的值。

问题是——我需要把同步放在 getter 之前吗?看起来这不会导致任何问题 - 内存应该是最新的,并且 Thread2 的本地缓存应该由 Thread1 失效和更新,但我不确定。

4

6 回答 6

4

如果您在“便宜的读写锁”中制作可变易失性详细信息,那会很好

于 2010-12-01T22:45:12.647 回答
4

与其想知道,为什么不直接使用java.util.concurrent 中的原子引用呢?

(对于它的价值,我对happens-before的阅读并不能保证Thread2会看到可变的更改,除非它也使用同步......但我总是对JLS的那部分感到头疼,所以使用原子引用)

于 2010-12-01T22:34:12.307 回答
1

如果您非常担心读取线程中的性能,那么您所做的就是使用适当的同步或易失性或原子引用读取一次值。然后将值分配给一个普通的旧变量。

对普通变量的分配保证在原子读取之后发生(因为它怎么能得到这个值?)如果这个值永远不会被另一个线程再次写入,那么你就完成了。

于 2010-12-01T23:15:18.903 回答
1

您是否绝对确定只有在调用 setter 后才会调用 getter?如果是这样,则不需要同步 getter,因为不需要同步并发读取。

如果有可能同时调用 get 和 set ,那么您肯定需要将两者同步。

于 2010-12-01T22:34:20.463 回答
1

我认为你应该从正确的东西开始,然后当你知道你有问题时进行优化。除非几纳秒太长,否则我只会使用 AtomicReference。;)

public static void main(String... args) {
    AtomicReference<String> ars = new AtomicReference<String>();
    ars.set("hello");
    long start = System.nanoTime();
    int runs = 1000* 1000 * 1000;
    int length = test(ars, runs);
    long time = System.nanoTime() - start;
    System.out.printf("get() costs " + 1000*time / runs + " ps.");
}

private static int test(AtomicReference<String> ars, int runs) {
    int len = 0;
    for (int i = 0; i < runs; i++)
        len = ars.get().length();
    return len;
}

印刷

get() costs 1219 ps.

ps 是皮秒,是百万分之一微秒。

于 2010-12-01T23:44:49.837 回答
0

这可能永远不会导致不正确的行为,但是除非您还保证线程启动的顺序,否则您不一定能保证编译器在 Thread1 中写入之前没有重新排序 Thread2 中的读取。更具体地说,整个 Java 运行时只需要保证线程的执行就像它们是串行运行一样。因此,只要线程在优化下串行运行相同的输出,整个语言堆栈(编译器、硬件、语言运行时)几乎可以做任何它想做的事情。包括允许 Thread2 缓存LockQuestion.getMutable().

在实践中,如果发生这种情况,我会感到非常惊讶。如果您想保证不会发生这种情况,请在构造函数中LockQuestion.mutable声明final并初始化。或使用以下成语

private static class LazySomethingHolder {
  public static Something something = new Something();
}

public static Something getInstance() {
  return LazySomethingHolder.something;
}
于 2010-12-01T23:09:10.073 回答