Java 基于静态和实例方法的同步

Java 基于静态和实例方法的同步,java,thread-safety,Java,Thread Safety,我对同步实例方法和静态方法感到困惑。 我想编写一个线程安全类,如下所示: public class safe { private final static ConcurrentLinkedQueue<Object> objectList= new ConcurrentLinkedQueue<Object>(); /** * retrieves the head of the object and prints it */ pub

我对同步实例方法和静态方法感到困惑。 我想编写一个线程安全类,如下所示:

public class safe {

  private final static ConcurrentLinkedQueue<Object> objectList=
      new ConcurrentLinkedQueue<Object>();

  /**
   * retrieves the head of the object and prints it
   */
    public synchronized static  void getHeadObject() {
      System.out.println(objectList.peek().toString());

    }

    /**
     * creates a new object and stores in the list.
     */
    public synchronized void addObject() {
      Object obj=new Object();
      objectList.add(obj);

    }
}
公共类安全{
私有最终静态ConcurrentLinkedQueue对象列表=
新建ConcurrentLinkedQueue();
/**
*检索对象的头部并打印它
*/
公共同步静态void getHeadObject(){
System.out.println(objectList.peek().toString());
}
/**
*创建新对象并存储在列表中。
*/
公共同步的void addObject(){
Object obj=新对象();
objectList.add(obj);
}
}
在静态方法上同步将锁定safe.class lock,在实例方法上同步将锁定safe.class lock,因此将达到不一致的状态


如果我想为下面的代码片段实现一致的状态,那么该如何实现呢?

编辑:我假设您的意思是
队列对象列表
,而不是
ConcurrentLinkedQueue对象列表
ConcurrentLinkedQueue
已经为您实现了所有线程安全,这意味着您可以调用
objectList.peek()
所有您想要的,而不必担心争用条件。如果您正在开发多线程程序,这是非常好的,但对于学习线程安全性来说却不是那么好

假设一次有一个线程在对象的一个实例上运行,那么您的方法不需要进行
同步
,但是如果您需要有多个引用相同静态类变量的类实例,则需要对类变量进行
同步
,如下所示:

public static void getHeadObject() {
    synchronized(safe.objectList) {
        System.out.println(objectList.peek().toString());
    }
}
这将锁定
对象列表
,并且一旦程序进入同步块,就不允许在任何其他线程中读取或写入它。对要同步的所有其他方法执行相同的操作

注意:

但是,由于您只执行一个简单的get操作
List.peek()
,因此实际上不需要通过
objectList
进行同步,因为在竞争条件下,它将获取
列表的一个值或另一个值。竞态条件的问题是当执行多个复杂的读/写操作时,值在它们之间变化

例如,如果您有一个类
PairInt
,其中包含
PairInt.x
PairInt.y
字段,约束条件是
x=2y
,并且您希望执行此操作

System.out.println(myIntPair.x.toString() + ", " + myIntPair.y.toString());
另一个线程同时更新
x
y
的值

myIntPair.y = y + 3;
myIntPair.x = 2 * y;
在读取线程的
myIntPair.x.toString()
myIntPair.y.toString()
之间修改了写入线程的
myIntPair
,您可能会得到一个类似
(10,8)
的输出,这意味着如果您在假设
x==2*y
的情况下操作,程序可能会崩溃

在这种情况下,您的读取需要使用
synchronized
,但对于在队列中添加或删除而未修改的简单
对象上更简单的
peek()
,在大多数情况下可以删除
synchronized
。事实上,对于
string
int
bool
等,应该删除用于简单读取的
同步
条件


但是,对于非显式线程安全的操作,即已经由java处理的操作,写入应该始终是同步的。一旦您获取了多个资源,或者要求您的资源在整个操作过程中保持不变,就像您对其执行多行逻辑一样,那么您必须首先使用
同步的

,ConcurrentLinkedQueue不需要显式同步。看

其次,您始终可以同步正在访问的对象:

public class safe {

      private final static ConcurrentLinkedQueue<Object> objectList=
          new ConcurrentLinkedQueue<Object>();

      /**
       * retrieves the head of the object and prints it
       */
     public static  void getHeadObject() {
         synchronized(objectList){
          System.out.println(objectList.peek().toString());
         }

     }

        /**
         * creates a new object and stores in the list.
         */
     public void addObject() {
          Object obj=new Object();
       synchronized(objectList){
          objectList.add(obj);
       }

     }
}
公共类安全{
私有最终静态ConcurrentLinkedQueue对象列表=
新建ConcurrentLinkedQueue();
/**
*检索对象的头部并打印它
*/
公共静态void getHeadObject(){
已同步(对象列表){
System.out.println(objectList.peek().toString());
}
}
/**
*创建新对象并存储在列表中。
*/
public void addObject(){
Object obj=新对象();
已同步(对象列表){
objectList.add(obj);
}
}
}
一些评论:

  • Java约定:
    • 类名应为CamelCase(即调用您的类
      Safe
      ,而不是
      Safe
    • 在方法声明中,
      static
      位于
      synchronized
      之前
    • 静态
      在字段声明中位于
      最终
      之前
  • 正如其他人已经说过的,
    ConcurrentLinkedQueue
    已经是线程安全的,因此在您给出的示例中不需要同步
  • 将静态方法和非静态方法混合使用的方式看起来很奇怪
  • 假设您的实际用例更复杂,并且您需要一个方法来运行原子操作,那么您的代码就不能工作了,正如您所指出的,因为两个同步的方法不在同一个监视器上同步:
为了回答您的具体问题,您可以使用单独的静态对象作为锁:

public class Safe {

    private static final ConcurrentLinkedQueue<Object> objectList =
            new ConcurrentLinkedQueue<Object>();
    // lock must be used to synchronize all the operations on objectList
    private static final Object lock = new Object();

    /**
     * retrieves the head of the object and prints it
     */
    public static void getHeadObject() {
        synchronized (lock) {
            System.out.println(objectList.peek().toString());
        }
    }

    /**
     * creates a new object and stores in the list.
     */
    public void addObject() {
        synchronized (lock) {
            Object obj = new Object();
            objectList.add(obj);
        }
    }
}
公共类安全{
私有静态最终ConcurrentLinkedQueue对象列表=
新建ConcurrentLinkedQueue();
//锁必须用于同步objectList上的所有操作
私有静态最终对象锁=新对象();
/**
*检索对象的头部并打印它
*/
公共静态void getHeadObject(){
已同步(锁定){
System.out.println(objectList.peek().toString());
}
}
/**
*创建新对象并存储在列表中。
*/
普布利
public class Safe {

    private static final ConcurrentLinkedQueue<Object> objectList =
            new ConcurrentLinkedQueue<Object>();
    // lock must be used to synchronize all the operations on objectList
    private static final Object lock = new Object();

    /**
     * retrieves the head of the object and prints it
     */
    public static void getHeadObject() {
        synchronized (lock) {
            System.out.println(objectList.peek().toString());
        }
    }

    /**
     * creates a new object and stores in the list.
     */
    public void addObject() {
        synchronized (lock) {
            Object obj = new Object();
            objectList.add(obj);
        }
    }
}