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:
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:
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