java getter是线程安全的吗?

Cru*_*her 0 java multithreading

可以同步所有改变对象状态但不同步任何原子的方法吗?在这种情况下,只返回一个字段?

考虑:

public class A
{
   private int a = 5;

   private static final Object lock = new Object();

   public void incrementA()
   {
       synchronized(lock)
       {
           a += 1;
       }
   }

   public int getA()
   {
       return a;
   }
}
Run Code Online (Sandbox Code Playgroud)

我听到人们认为它是可能的getA(),并incrementA()在大致相同的时间被调用,有getA()返回错误的事情.然而,似乎在它们同时被调用的情况下,即使吸气剂同步,你也可能得到错误的东西.实际上,如果同时调用这些"正确的东西",它们似乎甚至没有被定义.对我来说最重要的是国家保持一致.

我也听说过关于JIT优化的讨论.给定上述类的实例和以下代码(代码将依赖于a在另一个线程中设置):

while(myA.getA() < 10)
{
    //incrementA is not called here
}
Run Code Online (Sandbox Code Playgroud)

显然,合法的JIT优化将其更改为:

int temp = myA.getA();
while(temp < 10)
{
    //incrementA is not called here
}
Run Code Online (Sandbox Code Playgroud)

这显然会导致无限循环.为什么这是合法的优化?如果a不稳定,这会非法吗?

更新

我做了一些测试.

public class Test
{
   private int a = 5;

   private static final Object lock = new Object();

   public void incrementA()
   {
       synchronized(lock)
       {
           a += 1;
       }
   }

   public int getA()
   {
       return a;
   }

   public static void main(String[] args)
   {
       final Test myA = new Test();
       Thread t = new Thread(new Runnable(){
        public void run() {
            while(true)
            {
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
                myA.incrementA();
            }
        }});
       t.start();
       while(myA.getA() < 15)
       {
           System.out.println(myA.getA());
       }
   }
}
Run Code Online (Sandbox Code Playgroud)

使用几种不同的睡眠时间,即使a不易变,也能正常工作.这当然不是决定性的,它仍然可能是合法的.有没有人有一些可以触发这种JIT行为的例子?

Gra*_*ray 7

可以同步所有改变对象状态但不同步任何原子的方法吗?在这种情况下,只返回一个字段?

取决于具体细节.重要的是要意识到同步做了两件重要的事情.它不仅仅与原子性有关,而且由于内存同步也需要它.如果一个线程更新该a字段,则由于本地处理器上的内存缓存,其他线程可能看不到更新.使该int a领域volatile解决了这个问题.制作get和set方法synchronized也是如此,但它更昂贵.

如果您希望能够更改和读取a多个线程,最好的机制是使用AtomicInteger.

private AtomicInteger a = new AtomicInteger(5);
public void setA(int a) {
   // no need to synchronize because of the magic of the `AtomicInteger` code
   this.a.set(a);
}
public int getA() {
    // AtomicInteger also takes care of the memory synchronization
    return a.get();
}
Run Code Online (Sandbox Code Playgroud)

我听说有人认为getA()和setA()可能在大致相同的时间被调用并且让getA()返回错误的东西.

这是正确的,但如果稍后调用它,则可能会得到错误的值.一个糟糕的缓存值可以永远坚持下去.

这显然会导致无限循环.为什么这是合法的优化?

这是一种合法的优化,因为以异步方式运行自己的内存缓存的线程是查看性能改进的重要原因之一.如果所有内存访问都与主内存同步,则不会使用每CPU内存缓存,并且线程程序运行速度会慢得多.

如果一个波动的话,这会不合法吗?

如果有某种方式a可以改变 - 可能是另一个线程,这是不合法的.如果a是最终的那么JIT可以进行优化.如果avolatile或者get方法被标记为synchronized那么它肯定不是合法的优化.