【问题标题】:Synchronization of a variable变量的同步
【发布时间】:2015-02-23 10:20:20
【问题描述】:

在这个程序中,对象jai被一个线程访问,同时它也被另一个线程修改。这是正确的工作方式吗?不需要让对象同步吗?

public class SameObjectModification {

    private static int i = 0;

    private static Jai jai;

    static class Jai {

        private final int a;

        public Jai( int a ) {
            this.a = a;
        }

        public void print() {
            System.out.println( "value ==> " + a );
        }
    }

    public static void main( String[] args ) {

        Thread t1 = new Thread() {

            @Override
            public void run() {
                while ( true ) {
                    if ( jai != null )
                        jai.print();
                }
            }
        };

        Thread t2 = new Thread() {

            @Override
            public void run() {
                while ( true ) {
                    jai = new Jai( i++ );
                }
            }
        };

        t1.start();
        t2.start();
    }
}

【问题讨论】:

  • 你想达到什么目的?
  • 不,不是。您正在从不同的线程访问状态而没有同步。所以你会遇到可见性问题。
  • 这取决于你想要什么。如果你想实现互斥,那么你必须同步它..

标签: java synchronization synchronized


【解决方案1】:

事实上,变量jai 是按顺序引用了许多对象。在第二个线程的循环的每次迭代中,都会创建一个新对象并将其分配给变量。因此,在 jai 上同步将无济于事。

如果要确保t2 线程创建的所有对象都由t1 处理,则需要锁定t1 并等待t2 创建对象或使用一种队列。阅读java.util.concurrent.BlockingQueue

更一般地说,这是Producer-Consumer 问题的一个示例。

【讨论】:

    【解决方案2】:

    你的例子的这个保存版本

    private static int i = 0;
    
    private static AtomicReference<Jai> jai = new AtomicReference<Jai>();
    
    static class Jai {
    
        private final int a;
    
        public Jai( int a ) {
            this.a = a;
        }
    
        public void print() {
            System.out.println( "value ==> " + a );
        }
    }
    
    public static void main( String[] args ) {
    
        Thread t1 = new Thread() {
    
            @Override
            public void run() {
                while ( true ) {
                    Jai tmp = jai.get();
                    if ( tmp != null )
                        tmp.print();
                }
            }
        };
    
        Thread t2 = new Thread() {
    
            @Override
            public void run() {
                while ( true ) {
                    jai.set(new Jai( i++ ));
                }
            }
        };
    
        t1.start();
        t2.start();
    }
    

    但实施取决于您的计划。 您应该仅将 AtomicReference 用于不可变或线程安全的类。

    在其他情况下,您可以使用不同的锁、可变字段、不同的原子实现(例如 AtomicLong)或同步。

    但您应该记住,原子比锁快,而锁比同步快。

    【讨论】:

    • 我想知道为什么运行此代码时没有任何异常。
    • 您的代码不会出现任何异常,因为引用下的操作通常是 CPU 架构(例如 x86)的原子操作。但是如果你在异国情调的 cpu 上运行你的代码,你会得到异常。
    【解决方案3】:

    目前尚不清楚您要实现什么,但您可以通过使用原子来为每个 Jai 获得唯一的 a 值。

    private static AtomicInteger i = new AtomicInteger(0);
    
    private static Jai jai;
    
    static class Jai {
    
        private int a;
        public Jai() {
            this.a = i.incrementAndGet();
        }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-10-22
      • 2019-07-07
      • 2020-04-17
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多