paul
paul

Reputation: 4487

Java: Sharing a variable across multiple threads

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

Answers (4)

Punit Tiwan
Punit Tiwan

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

bvdl
bvdl

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

Sofo Gial
Sofo Gial

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

Mick
Mick

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

Related Questions