Java 如何应用同步?

Java 如何应用同步?,java,synchronization,Java,Synchronization,我有UI自动化测试。测试涉及三个实体: 数据对象类-要在表单中填写的数据。在此,页面上的每个表单都可以由不同的数据对象表示。 Helper类-在第页的表单中填充数据 测试类-它使用数据对象和助手类来执行测试 以下是测试的简化版本- public class ParallelDataObject { HelperClass helperClass = new HelperClass(); Data data; @BeforeMethod public void

我有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类中删除了所有同步。当我并行运行类(而不是方法)时。测试结果与预期一致。为什么我在并行执行类时不运行并发性,即使它们使用相同的帮助器类和数据对象

考虑使用。这样,每个线程都有自己的
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;
    }
}

包装整个方法体,考虑在方法声明中使用<代码>同步< /代码>关键字(更可读)。

  • 不再需要使用
    ThreadLocal
    s进行同步

  • 测试无状态 @gpeche提出了一个很好的建议,即测试应该是独立的。不幸的是(why,oh why!?)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();
        }
    
    }
    

    HelperClass
    Data
    是线程安全的

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

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


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

    请注意,您可以使用public synchronized boolean getFlag()作为将方法代码包装为synchronized(此)的缩写。我强烈建议使每个测试独立。这应该不是很难,只需为每个测试使用所有内容的新实例即可。否则会使您的测试更复杂、更难维护。我想您的陈述——“在一个测试中所做的更改(在一个线程中)会被其他测试看到”回答了我的另一个问题,即为什么我在并行运行类而不是并行运行方法时不会遇到并发性问题。@Tarun:看看我上次的更新(底部的代码片段)-我认为这是受gpecheAwesome启发的简单(因此是最好的)解决方案,正是我所寻找的。
    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();
        }
    
    }