可以使用两个嵌套的同步块来锁定数组的两个单元格以进行原子操作吗?

时间:2018-02-21 12:09:58

标签: java multithreading synchronized

我有一个类Repository,它分配表示为cells数组字段值的资源。该方法将资源从一个单元移动到另一个单元。

我需要确保当我们将资源从一个单元格移动到另一个单元格时,不能对所讨论的单元格执行其他移动操作。也就是说,如果一个单元(原始单元或目标单元)参与move操作,我们必须等到当前操作完成后再对这些单元执行另一个move操作。对不同的单元格对并行执行move应该没有限制,例如,move(cells[1], cells[2], 5)move(cells[4], cells[7], 9)可以并行执行。

我想使用两个嵌套的synchronized块来保护原始和目标单元格。我认为我们不需要使用wait / notifyAll,因为我们仍在使用synchronized

我是在正确的轨道上吗?

这是代码(moveOriginal是原始方法,moveSynchronized是受保护的方法:

public class Repository {
    private Integer[] cells;

    public Repository(int size, int initialValue) {
        cells = new Integer[size];
        for(int i = 0; i < size; i++) {
            cells[i] = initialValue;
        }
    }

    public void moveOriginal(int from, int to, int amount) {
        if(cells[from] >= amount) {
            cells[from] = cells[from] - amount;
            cells[to] = cells[to] + amount;
        }
    }

    public void moveSynchronized(int from, int to, int amount) {
        synchronized(cells[from]) {
            synchronized (cells[to]) {
                if(cells[from] >= amount) {
                    cells[from] = cells[from] - amount;
                    cells[to] = cells[to] + amount;
                }
            }
        }
    }
}

2 个答案:

答案 0 :(得分:3)

这种方法存在多个问题。

  1. $args = array( 'post_type' => 'course', 'post_status' => 'publish' ); if(!empty($cs_course_cate)){ $args = array('tax_query' => array( array( 'taxonomy' => 'course-category', 'terms' => $cs_course_cate, 'field' => 'term_id', ) )); } if(!empty($cs_campus)){ $args = array('meta_query' => array( array( 'key' => 'cs_course_campus_id', 'value' => $cs_campus, 'compare' => 'LIKE', ), ) ); } 锁定了synchronized的值cell[x],所以只要你这样做

    Integer

    cells[from] = cells[from] - amount; 上的同步会锁定另一个对象。

  2. cells[from]实习对象代表小值。锁定这样的共享对象可能会将您锁定在完全错误的单元格之外。

  3. 共享Integer个对象的可用性超出了您的主题。另一个线程可以获取您同步的共享整数,并锁定Integer,而无法访问您班级的私有数据结构。
  4. 如果要进行单元级锁定,请创建一个专门用于锁定相应单元格的对象数组;不要锁定单元格值。

答案 1 :(得分:2)

不,你不能这样做。为此,您需要锁定阵列本身,以防止并行操作。

cells是不可变的,意味着Integer会在其中放置不同的对象,而不是修改其中的cells[to] = cells[to] + amount。当Integer在不同时间引用不同的对象时,这将导致问题。

解决此问题的最简单方法是使用synchronized(cells[to])初始化Object[] lockArray = new Object[size];并对其进行同步。它们不会在您的业务逻辑中发生变化。

在任何情况下,您都需要嵌套的Objects范围。您还需要做的是define an order,例如始终首先在较小的值上同步。否则,当多个线程同时尝试执行synchronized之类的操作时,您将遇到死锁。

move(1, 2); move(2, 1);