Reputation: 4487
Background: I am running automation tests in parallel. Multiple browsers get launch in the same number of threads i.e. 1 browser is 1 thread, using forking in pom.xml.
Below plugin in pom.xml
creates an equal number of Parallel**IT.class
as thread(fork) count is.
All these classes are executed at once parallel. So, it seems whenever I create a volatile variable
or AtomicInteger
each thread creates its own of these and so concept sharing a variable across multiple threads is not working.
<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>
I want only 1 thread to access the "prepare test data" function and set flag
to false
, when other threads see flag
as false
they do not attempt to prepare test data.
I am following a tutorial https://www.youtube.com/watch?v=WH5UvQJizH0 to implement synchronization
using a volatile
variable. Maybe I am making some mistake, but all threads are printing System.out.println("Preparing test data");
Try 1: Volatile and Synchronization
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();
}
};
Try 2: Atomic and Synchronization
AtomicInteger flag = new AtomicInteger(0);
private EventHandler<TestRunStarted> prepareTestData = event -> {
if(flag.get()==0) {
System.out.println("Preparing test data");
value.incrementAndGet();
}
Upvotes: 1
Views: 1031
Reputation: 106
The "flag" and "lockObject" needs to be "static" so that you will have only one instance for each object. Although if you have singleton object then you don't have to use "static" for "flag" but "lockObject" should be static.
Also, synchronize using "lockObject" inside "prepareTestData". This indicate java that only one thread should be accessing the code beyond this point. Once one thread goes inside, others will be waiting for that thread to return before going in.
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;
}
}
}
Hope this will answer your question.
Upvotes: 0
Reputation: 421
If I were you I would implement the a Mutex lock, when accessing the flag variable. So before reading the value of the flag, or changing its value, the thread has to acquire the lock. This way they never read it at the same time or write a new value while an old one is till being read etc.
EDIT: More explanation
@paul To explain what the lock does: It is basically a ball that can be tossed around by threads. So if you sit in a circle with a group of people, and you have the ball it is your turn to speak about "X". Then once you are done speaking you put the ball in the middle of the circle, and it remains there till the same person or someone else wants the ball again and takes it or waits till it is available and may then speak about "X". In your case a thread must have the lock to change or read the variable flag, so what you would do is:
Mutex lock = new Mutex();
lock.aquire();
if ( flag == something ){
//do something
}
mutex.release()
or if you are changing the flag.
lock.aquire();
flag = something;
lock.release();
As you can see the lock is shared between threads. So it is created in the class that manages the threads, and passed on to the Runnable Objects or methods that are started in the threads.
So:
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();
Good luck :)
Upvotes: 1
Reputation: 703
You have synchronized the wrong method. You could try something like this instead:
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
}
Upvotes: 0
Reputation: 973
If you do a test (flag.get()==0) and an action, you have two synchronize both of these things. Just mark your whole prepareTestData-method as synchronized?
Upvotes: 0