Java: совместное использование переменной в нескольких потоках - PullRequest
1 голос
/ 23 мая 2019

Справочная информация: Я параллельно выполняю тесты автоматизации.Несколько браузеров запускаются в одинаковом количестве потоков, т. Е. 1 браузер является 1 потоком, используя разветвление в pom.xml.

Под плагином в pom.xml создается такое же число Parallel**IT.class, как и количество потоков (разветвлений)is.
Все эти классы выполняются одновременно параллельно.Таким образом, кажется, что всякий раз, когда я создаю volatile variable или AtomicInteger, каждый поток создает свои собственные, поэтому концепция совместного использования переменной между несколькими потоками не работает.

                <plugin>
                        <artifactId>maven-failsafe-plugin</artifactId>
                        <version>${maven.failsafe.plugin}</version>
                        <configuration>
                            <systemPropertyVariables>
                                <webdriver.base.url>${webdriver.base.url}</webdriver.base.url>
                                                                </systemPropertyVariables>
                            <argLine>
                                -javaagent:${settings.localRepository}/org/aspectj/aspectjweaver/${aspectj.version}/aspectjweaver-${aspectj.version}.jar
                            </argLine>
                            <forkCount>5</forkCount>
                            <reuseForks>true</reuseForks>
                            <includes>
                                <include>**/Parallel*IT.class</include>
                            </includes>
                            <perCoreThreadCount>true</perCoreThreadCount>
                            <properties>
                                <property>
                                    <name>listener</name>
            <value>ru.yandex.qatools.allure.junit.AllureRunListener</value>
                                </property>
                            </properties>
                        </configuration>
                        <executions>
                            <execution>
                                <goals>
                                    <goal>integration-test</goal>
                                    <goal>verify</goal>
                                </goals>
                            </execution>
                        </executions>
                    </plugin>


Я хочу только1 поток для доступа к функции «подготовить тестовые данные» и установите flag на false, когда другие потоки видят flag как false, они не пытаются подготовить тестовые данные.

Я следуюучебное пособие https://www.youtube.com/watch?v=WH5UvQJizH0 по реализации synchronization с использованием переменной volatile.Возможно, я делаю какую-то ошибку, но все потоки печатают System.out.println("Preparing test data");

Попытка 1: энергозависимость и синхронизация

volatile boolean flag = false;

    public synchronized void setFlagTofalse(){
        System.out.println("Inside sync block");
          this.flag = true;
    }
    // works before class only once
    private EventHandler<TestRunStarted> prepareTestData = event -> {
            if(flag==false) {
                System.out.println("Preparing test data");              
                setFlagTofalse();
            }
    };

Попытка 2: атомарное иСинхронизация

AtomicInteger flag = new AtomicInteger(0);

    private EventHandler<TestRunStarted> prepareTestData = event -> {
        if(flag.get()==0) {
            System.out.println("Preparing test data");
            value.incrementAndGet();
        }

Ответы [ 4 ]

1 голос
/ 23 мая 2019

На вашем месте я бы реализовал блокировку Mutex при доступе к переменной flag. Поэтому, прежде чем прочитать значение флага или изменить его значение, поток должен получить блокировку. Таким образом, они никогда не читают его в одно и то же время и не записывают новое значение, пока старое не будет прочитано и т. Д.

РЕДАКТИРОВАТЬ: Больше объяснений

@ paul Чтобы объяснить, что делает блокировка: это в основном шар, который можно перебросить нитями. Так что, если вы сидите в кругу с группой людей, и у вас есть мяч, ваша очередь говорить о «Х». Затем, когда вы закончите говорить, вы помещаете мяч в центр круга, и он остается там до тех пор, пока тот же человек или кто-то еще не захочет снова получить мяч и не заберет его или не дождется, пока он не станет доступным, и затем может говорить о «Х». В вашем случае поток должен иметь блокировку для изменения или чтения флага переменной, поэтому вы должны сделать следующее:

Mutex lock = new Mutex();
lock.aquire();
if ( flag == something ){
    //do something
}
mutex.release()

или если вы меняете флаг.

lock.aquire();
flag = something;
lock.release();

Как видите, блокировка разделяется между потоками. Поэтому он создается в классе, который управляет потоками, и передается Runnable Objects или методам, которые запускаются в потоках.

Итак:

Mutex lock = new Mutex();
Runnable1 r1 = new Runnable1(lock);
Runnable2 r2 = new Runnable2(lock);
//use the lock in the methods of t1 and t2 that use your volitile var
Thread t1 = new Thread(r1);
Thread t2 = new Thread(r2);

t1.start();
t2.start();

//the join wait for completion of the runnable method in your class.
t1.join();
t2.join();

Удачи:)

0 голосов
/ 23 мая 2019

«flag» и «lockObject» должны быть «static», чтобы у вас был только один экземпляр для каждого объекта. Хотя, если у вас есть одноэлементный объект, вам не нужно использовать «static» для «flag», но «lockObject» должен быть статическим.

Кроме того, синхронизируйте, используя "lockObject" внутри "prepareTestData". Это указывает Java, что только один поток должен получить доступ к коду за пределами этой точки. Как только один поток войдет внутрь, другие будут ждать возврата этого потока, прежде чем войти.

private static Object lockObject = new Object();
private static Boolean flag = Boolean.FALSE;

private void prepareTestData() {
   synchronized(lockObject) { //This will keep other threads waiting
      if(!flag) {
          //prepare data
          flag = Boolean.TRUE;
       }
   }
}

Надеюсь, это ответит на ваш вопрос.

0 голосов
/ 23 мая 2019

Вы синхронизировали неправильный метод.Вы можете попробовать что-то вроде этого:

volatile boolean flag = false;

public void setFlagTofalse(){
    System.out.println("Inside sync block");
    this.flag = true;
}
// works before class only once
private EventHandler<TestRunStarted> prepareTestData = event -> prepareData();

private synchronized void prepareData() {
    if(!flag) {
        System.out.println("Preparing test data");
        setFlagTofalse();
    }
    //prepare data here
}
0 голосов
/ 23 мая 2019

Если вы выполняете тест (flag.get () == 0) и действие, у вас есть два способа синхронизировать обе эти вещи.Просто пометьте весь ваш метод prepareTestData как синхронизированный ?

...