November 21, 2012



Use of ThreadLocal in JMeter

Suppose:
- We have a class called Person and a member variable called name
- We have two Java samplers in JMeter. First sampler will set the name. The second sampler will read the name.

We can make the name variable to be a static variable. However if there are multiple threads, then racing conditions will occur and the data will be unreliable. What we need is a ThreadLocal. Unlike global static variables, ThreadLocal variables are static only within a thread's life and thus, multiple threads will have its own copy of the Person.name variable.

package jmeter.threadlocal;

public class Person {
    String name;
  
    private Person() {
    }

    public static ThreadLocal<Person> threadLocal = new ThreadLocal<Person>() {
        @Override
        protected Person initialValue() {
            return new Person();
        }
    };
   
    public static void setName(String newName){
        threadLocal.get().name = newName;
    }
   
    public static String getName(){
        return threadLocal.get().name;
    }

}


First Java sampler that will set the name:

package jmeter.threadlocal;

import java.util.Random;

import org.apache.jmeter.config.Arguments;
import org.apache.jmeter.protocol.java.sampler.AbstractJavaSamplerClient;
import org.apache.jmeter.protocol.java.sampler.JavaSamplerContext;
import org.apache.jmeter.samplers.SampleResult;

public class Test1 extends AbstractJavaSamplerClient {

    @Override
    public SampleResult runTest(JavaSamplerContext context) {
        SampleResult res = new SampleResult();
        res.sampleStart();

        String name = context.getParameter("name")
                + (new Random()).nextInt(Integer.MAX_VALUE)
                + Integer
                        .toHexString(((new Random().nextInt(Integer.MAX_VALUE))));
        super.getLogger().info(
                Thread.currentThread().getId() + " Setting name: " + name);
        Person.setName(name);
        super.getLogger().info(
                Thread.currentThread().getId() + " Set name: " + name);
        res.sampleEnd();
        return res;
    }

    public Arguments getDefaultParameters() {
        Arguments args = new Arguments();
        args.addArgument("name", "");
        return args;
    }
}


Second Java sampler that will read the name:

package jmeter.threadlocal;

import org.apache.jmeter.protocol.java.sampler.AbstractJavaSamplerClient;
import org.apache.jmeter.protocol.java.sampler.JavaSamplerContext;
import org.apache.jmeter.samplers.SampleResult;

public class Test2 extends AbstractJavaSamplerClient {

    @Override
    public SampleResult runTest(JavaSamplerContext context) {
        SampleResult res = new SampleResult();
        res.sampleStart();

        super.getLogger().info(
                Thread.currentThread().getId() + " Getting bytes: "
                        + Person.getName());

        res.sampleEnd();
        return res;
    }

}


Compile and package the .class files into a jar. Place it in $JMETER_INSTALLDIR\lib\ext. Create a JMeter test plan as follows:

   




JMeter logs:

2012/11/21 11:11:47 INFO  - jmeter.protocol.java.sampler.AbstractJavaSamplerClient: 29 Setting name: parthy5192850885cfaf9a9
2012/11/21 11:11:47 INFO  - jmeter.protocol.java.sampler.AbstractJavaSamplerClient: 29 Set name: parthy5192850885cfaf9a9
2012/11/21 11:11:47 INFO  - jmeter.protocol.java.sampler.AbstractJavaSamplerClient: 28 Setting name: parthy148372871654161351
2012/11/21 11:11:47 INFO  - jmeter.protocol.java.sampler.AbstractJavaSamplerClient: 28 Set name: parthy148372871654161351
2012/11/21 11:11:47 INFO  - jmeter.protocol.java.sampler.AbstractJavaSamplerClient: 29 Getting name: parthy5192850885cfaf9a9
2012/11/21 11:11:47 INFO  - jmeter.protocol.java.sampler.AbstractJavaSamplerClient: 28 Getting name: parthy148372871654161351