Java 同步读并行,一次写入一个

Java 同步读并行,一次写入一个,java,synchronization,thread-safety,Java,Synchronization,Thread Safety,我有一个对象obj,它经常被多个线程读取,但只有一个线程周期性地更新它。更新发生在一段很长的时间间隔之后(比如10分钟) 数据的跨国性较小,这意味着如果读取线程在一段时间内获得陈旧数据(旧数据),那么就完全可以了 现在我考虑使用以下方法进行同步: final Object lock = new Object(); private MyObject obj = new MyObject(); //this is the data public String getDataFieldName(){

我有一个对象obj,它经常被多个线程读取,但只有一个线程周期性地更新它。更新发生在一段很长的时间间隔之后(比如10分钟)

数据的跨国性较小,这意味着如果读取线程在一段时间内获得陈旧数据(旧数据),那么就完全可以了

现在我考虑使用以下方法进行同步:

final Object lock = new Object();
private MyObject obj = new MyObject(); //this is the data

public String getDataFieldName(){
 synchronized(lock){
   return this.obj.name;
 }
}


/*the following code is wrong right?As its just synchronizes the code for getting reference.But after getting reference read thread R1 may try to get data while write Thread is modifying data.Will that give exception?How to solve this? */
public String getData(){
 synchronized(lock){
   return this.obj;
 }
}

//only one thread can update.But how multipe threads can read at once?
public updateData(args ) {
  synchronized(lock){
    //do update
 }

}
我的问题如下:

  • 我不希望只有一个线程读取数据。读取应该是并行的。

  • 如何同步读写?如果写线程正在更新,而读线程正在读取,我不知道该如何获得一些异常。如果读线程获得一些旧数据,这是正常的 3) 如果读线程正在读,而写线程正在更新,我会得到异常吗?会有任何问题吗


  • 在这种情况下,您不需要任何同步。您只需执行以下操作:

    • 确保
      MyObject
      为,这意味着您永远不会更改对象中的任何值,而是每次更改对象时都会构造一个新的
      MyData
      对象。这可以防止任何人看到半变化的对象
    • 将obj声明为,以确保所有线程每次都看到更新的值

    如果遵循这些步骤,将永远不会因为并发读写而出现异常。

    在这种情况下,您不需要任何同步。您只需执行以下操作:

    • 确保
      MyObject
      为,这意味着您永远不会更改对象中的任何值,而是每次更改对象时都会构造一个新的
      MyData
      对象。这可以防止任何人看到半变化的对象
    • 将obj声明为,以确保所有线程每次都看到更新的值
    如果遵循这些步骤,将永远不会因为并发读写而出现异常

  • 使用volatile关键字,与同步不同,它不会获取锁,并将提供多个访问,反映一个线程到另一个线程的更新

  • 但是,最好进行某种同步,因为volatile不能确保防止数据上的竞争条件。所以,如果您不想使用同步,那么最好使用不可变对象

  • 例如:

    import java.util.Date;
    
    /**
    * Planet is an immutable class, since there is no way to change
    * its state after construction.
    */
    public final class Planet {
    
    
     //Final primitive data is always immutable.
    
      private final double fMass;
    
      private final String fName;
    
      private final Date fDateOfDiscovery;
    
      public Planet (double aMass, String aName, Date aDateOfDiscovery) {
         fMass = aMass;
         fName = aName;
         //make a private copy of aDateOfDiscovery
         //this is the only way to keep the fDateOfDiscovery
         //field private, and shields this class from any changes that 
         //the caller may make to the original aDateOfDiscovery object
         fDateOfDiscovery = new Date(aDateOfDiscovery.getTime());
      }
    
    
      public double getMass() {
        return fMass;
      }
    
    
      public String getName() {
        return fName;
      }
    
    
    
    
      public Date getDateOfDiscovery() {
        return new Date(fDateOfDiscovery.getTime());
      }
    
    
    }
    
  • 使用volatile关键字,与同步不同,它不会获取锁,并将提供多个访问,反映一个线程到另一个线程的更新

  • 但是,最好进行某种同步,因为volatile不能确保防止数据上的竞争条件。所以,如果您不想使用同步,那么最好使用不可变对象

  • 例如:

    import java.util.Date;
    
    /**
    * Planet is an immutable class, since there is no way to change
    * its state after construction.
    */
    public final class Planet {
    
    
     //Final primitive data is always immutable.
    
      private final double fMass;
    
      private final String fName;
    
      private final Date fDateOfDiscovery;
    
      public Planet (double aMass, String aName, Date aDateOfDiscovery) {
         fMass = aMass;
         fName = aName;
         //make a private copy of aDateOfDiscovery
         //this is the only way to keep the fDateOfDiscovery
         //field private, and shields this class from any changes that 
         //the caller may make to the original aDateOfDiscovery object
         fDateOfDiscovery = new Date(aDateOfDiscovery.getTime());
      }
    
    
      public double getMass() {
        return fMass;
      }
    
    
      public String getName() {
        return fName;
      }
    
    
    
    
      public Date getDateOfDiscovery() {
        return new Date(fDateOfDiscovery.getTime());
      }
    
    
    }
    

    你应该看看@Thomas,你应该把它作为一个答案,你应该看看@Thomas,你应该把它作为一个答案+1,但值得一提的是,这是可行的,只要它不是昂贵的建设。例如,它可能是一个大的HashMap缓存。同意,但由于他只每隔10分钟构造一个新对象,因此它必须是一个非常大的对象才能成为问题。@SimonC,@Keppil:实际对象是通过从DB加载值来构造的,而且代价很高。@Keppil即使我每次构造新对象,我需要更新这个类中对象的引用。另外,我将使用synchronized getData方法,如上面代码中所示。如果我同步getData,那么一次只能有一个线程获得引用,对吗?@user978939:引用更新是一个原子操作,所以没关系。您根本不需要任何同步,无论是在getData()还是在setData()中。而且,每10分钟构造一个昂贵的对象很可能是很好的。+1,但值得一提的是,这只有在构造成本不高的情况下才可行。例如,它可能是一个大的HashMap缓存。同意,但由于他只每隔10分钟构造一个新对象,因此它必须是一个非常大的对象才能成为问题。@SimonC,@Keppil:实际对象是通过从DB加载值来构造的,而且代价很高。@Keppil即使我每次构造新对象,我需要更新这个类中对象的引用。另外,我将使用synchronized getData方法,如上面代码中所示。如果我同步getData,那么一次只能有一个线程获得引用,对吗?@user978939:引用更新是一个原子操作,所以没关系。您根本不需要任何同步,无论是在getData()还是在setData()中。而且,每10分钟构造一个昂贵的对象很可能是好的。