静态和实例方法的同步

Gay*_*tri 5 java thread-safety

我对同步实例方法和静态方法很困惑.我想写一个线程安全类如下:

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);

    }
}
Run Code Online (Sandbox Code Playgroud)

在静态方法上同步将锁定safe.class锁,并且在实例方法上进行同步将锁定此问题,因此将达到不一致的状态.

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

Han*_*s Z 1

编辑:我假设你的意思是Queue<Object> objectList而不是ConcurrentLinkedQueue<Object> objectList. ConcurrentLinkedQueue<Object>已经为您完成了所有线程安全,这意味着您可以调用objectList.peek()您想要的所有内容,而不必担心竞争条件。如果您正在开发多线程程序,这非常有用,但对于了解线程安全性则不太好。

您的方法不需要synchronized,假设您一次有一个线程对对象的一个​​实例进行操作,但是如果您需要有多个类实例,并且所有实例都引用同一个静态类变量,则需要对该synchronized类进行操作像这样的变量:

public static void getHeadObject() {
    synchronized(safe.objectList) {
        System.out.println(objectList.peek().toString());
    }
}
Run Code Online (Sandbox Code Playgroud)

这会锁定objectList,并且一旦程序位于同步块内,就不允许在任何其他线程中读取或写入它。对所有其他方法执行相同的操作synchronized

笔记:

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

例如,如果您有一个PairInt包含 aPairInt.xPairInt.y字段的类,并且具有x = 2y, 和您想要执行的约束

System.out.println(myIntPair.x.toString() + ", " + myIntPair.y.toString());
Run Code Online (Sandbox Code Playgroud)

另一个线程正在同时更新x和的值,y

myIntPair.y = y + 3;
myIntPair.x = 2 * y;
Run Code Online (Sandbox Code Playgroud)

并且写入线程myIntPair在读取线程之间进行修改myIntPair.x.toString()myIntPair.y.toString()您可能会得到类似于 的输出(10, 8),这意味着如果您的操作x == 2 * y可能会导致程序崩溃。

在这种情况下,您的读取需要使用 a synchronized,但对于更简单的事情,例如在队列中添加或删除而不是修改的peek()简单内容,在大多数情况下可以删除。事实上,对于、、等,应该删除简单读取的条件。objectsynchronizedstringintboolsynchronized

然而,写入应该始终synchronized针对非显式线程安全的操作,即已由 java 处理的操作。一旦您获取了多个资源,或者要求您的资源在执行多行逻辑时在整个操作过程中保持不变,那么您必须使用 synchronized