有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

java如何应用同步?

我有UI自动化测试。测试涉及三个实体:

数据对象类-要在表单中填写的数据。在此,页面上的每个表单可以由不同的数据对象表示
Helper类-在第页的表单中填充数据 测试类-它使用数据对象和助手类来执行测试

以下是测试的简化版本-

public class ParallelDataObject {

    HelperClass helperClass = new HelperClass();
    Data data;

    @BeforeMethod
    public void setTestData() {
        data = new Data();
        helperClass.setData(data);
    }

    @Test
    public void passM1() {
        helperClass.verifyFlag();
    }

    @Test
    public void failM2() {
        data.setFlag(false);
        helperClass.setData(data);
        helperClass.verifyFlag();
    }

    @Test
    public void passM3() {
        helperClass.verifyFlag();
    }

    @Test
    public void failM4() {
        data.setFlag(false);
        helperClass.setData(data);
        helperClass.verifyFlag();
    }
}

class HelperClass {
    Data data;  

    public void setData(Data data) {
        synchronized (data) {
            this.data = data;
        }
    }

    public void verifyFlag() {
        synchronized (data) {
            assert data.getFlag();
        }
    }
}

class Data {
    private boolean flag;

    public Data() {
        flag = true;
    }

    public Data setFlag(boolean flag) {
        synchronized (this) {
            this.flag = flag;
            return this;
        }
    }

    public boolean getFlag() {
        synchronized (this) {
            return flag;
        }
    }

当并行执行方法时,我遇到了奇怪的结果,因为数据不是线程安全的。然后我合并了同步块,但是我遇到了奇怪的结果。 我肯定我搞砸了这里应该如何使用同步。有什么见解吗

我又做了一次运动。我设置了与第一个测试类完全相同的另一个测试类。我从helper和data类中删除了所有同步。当我并行运行类(而不是方法)时。测试结果与预期一致。为什么我在并行执行类时不运行并发性,即使它们使用相同的帮助器类和数据对象


共 (2) 个答案

  1. # 1 楼答案

    考虑使用^{}。这样,每个线程都有自己的HelperClass副本。请注意,同步不同的方法不会给您带来任何好处—在一个测试(在一个线程中)中所做的更改可以被其他测试看到

    class ParallelDataObject {
    
        private final ThreadLocal<HelperClass> helperClassThreadLocal = new ThreadLocal<HelperClass>() {
    
            @Override
            protected HelperClass initialValue() {
                return new HelperClass(new Data());
            }
        };
    
        private HelperClass helperClass() {
            return helperClassThreadLocal.get();
        }
    
        @Test
        public void passM1() {
            helperClass().verifyFlag();
        }
    
        @Test
        public void failM2() {
            helperClass().getData().setFlag(false);
            helperClass().verifyFlag();
        }
    
    }
    
    class HelperClass {
    
        private final Data data;
    
        public HelperClass(Data data) {
            this.data = data;
        }
    
        public Data getData() {
            return data;
        }
    
        public void verifyFlag() {
            assert data.getFlag();
        }
    }
    
    class Data {
        private boolean flag = true;
    
        public Data setFlag(boolean flag) {
            this.flag = flag;
            return this;
        }
    
        public boolean getFlag() {
            return flag;
        }
    }
    

    其他改进:

    • passM3failM4是多余的
    • 因为HelperClass需要一个Data实例才能工作,所以它应该使用构造函数依赖项声明它
    • 使用时:

      synchronized(this)
      

      包装整个方法体,考虑在方法声明中使用^ {< CD7>}关键字(更可读)。

    • 不再需要与ThreadLocal同步

    测试无状态

    @gpeche提出了一个很好的建议,即测试应该是独立的。不幸的是(为什么,哦为什么!)JUnit为所有测试方法的执行重用相同的测试用例类实例(ParallelDataObject)。这意味着将任何有状态对象分配给测试用例类字段是危险的,必须避免

    在这种特殊情况下,OP必须在每个测试方法中创建一个新的HelperClass实例(事实上,这不是一个坏主意)

    class ParallelDataObject {
    
        @Test
        public void passM1() {
            final HelperClass helperClass = new HelperClass(new Data());
    
            helperClass.verifyFlag();
        }
    
        @Test
        public void failM2() {
            final Data data = new Data();
            data.setFlag(false);
    
            final HelperClass helperClass = new HelperClass(data);
    
            helperClass.verifyFlag();
        }
    
    }
    
  2. # 2 楼答案

    HelperClassData是线程安全的

    问题是您的一些测试方法执行了多个操作。而且,只要测试方法中的操作序列不同步,它就不是原子的

    例如,在failM4执行期间helperClass的状态可能会被其他线程修改

    我建议您不要在测试方法之间使用共享状态,因为同步将抵消并发测试执行的优势