Sharing data safely between Java threads

Am really starting to enjoy the power and portability of using Java in my load testing efforts, especially when the environments in which you work are often limited in terms of tool sets you’re allowed to use. For example the current Solaris environment I work in doesn’t have a C compiler native to the OS, so I am automatically limited in the amount of Perl code I can write, as I am lacking more powerful modules (that need to be compiled). Enter Java; it already comes installed and for the types of mid range services I’m working on (MQ, JMS etc) has loads of great APIs you can incorporate into your own custom test harnesses.

All good test harnesses are going to need to be multi-threaded, but I’ve had no idea on how to safely share data between threads! This problem has been annoying me for sometime, partly due to my own ignorance and lack of understanding of Java (indeed OO principles perhaps). But in any case, I was stuck in old (bad) habits of sharing data between threads. Normally I just assign a global variable but in OO design, that’s considered poor design, so here is my attempt at getting spawned threads of a ‘worker’ class sharing data via a common ‘datastore’ class.

I have a standard Java application which from main, instantiates an object of my ‘boss’ class.

/*
* Main.java
*/

public class Main {
public Main() {
}

public static void main(String[] args) {
BossThread boss = new BossThread();
boss.start();
}
}

The ‘boss’ class normally does a number of things, such as control thread execution logic and the like. It also serves as a common point in my design from which to launch ‘worker’ threads and also create the ‘datastore’ object. The BossThread looks a little like this.

/*
* BossThread.java
*/
import java.util.*;
import java.io.*;
import java.util.ArrayList;
import java.util.Iterator;

public class BossThread extends Thread {
private final ArrayList workers = new ArrayList();
private final DataStore dataStore = new DataStore();
private String threadType; // type of thread operation
private int numWorkersStarted = 0; // started thread counter
private int numThreads = 20; // number of threads to spawn

public BossThread() {
}

public void run() {
try {
threadType="put";
createWorkerThreads();
startWorkerThreads();

threadType="get";
createWorkerThreads();
startWorkerThreads();
} catch (Exception e) {
e.printStackTrace();
}
}

private void createWorkerThreads() throws Exception {
for (int i = 1; i <= numThreads; i++) {
String threadName = threadType + "_WORKER_" + i;
workers.add(new WorkerThread(threadName, dataStore, threadType));
}
}

private void startWorkerThreads() throws Exception {
int threadStartIndex=numWorkersStarted;
for (int i = threadStartIndex; i < numThreads+threadStartIndex; i++) {
WorkerThread worker = (WorkerThread) workers.get(i);
Thread.sleep(250 + (int) (Math.random() * 500));
worker.start();
numWorkersStarted++;
}
}
}

So you can see that early in the piece I instantiate an instance of the DataStore class, which is then passed as a parameter along with some custom fields like threadName and threadType to my WorkerThread class. I'm also using an ArrayList to simplify the organization, creation and starting of worker threads via some private methods.

The WorkerThread class looks like this.

/*
* WorkerThread.java
*/

public class WorkerThread extends Thread {
private DataStore dataStore;
private String threadType;
private String threadName;

public WorkerThread(String threadName, DataStore dataStore, String threadType) {
this.threadName = threadName;
this.dataStore = dataStore;
this.threadType = threadType;
}

public void run() {
if (threadType.equals("put")) putdataStore();
if (threadType.equals("get")) getdataStore();
}

private void putdataStore() {
for (int i = 0; i < 10; i++) {
dataStore.putResponseTime("key",i);
System.out.println(threadName + " put: " + i);
try {
sleep((int)(Math.random() * 1000));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}

private void getdataStore() {
int val = 0;
for (int i = 0; i < 10; i++) {
value = dataStore.getResponseTime("key");
System.out.println(threadName + " get: " + val);
try {
sleep((int)(Math.random() * 1000));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}

You can see that its constructor gets the instance of the 'datastore' object, which can then call on public methods of the DataStore class such as putdataStore and getdataStore.

Finally, the DataStore class looks like this.

/*
* DataStore.java
*/

import java.util.*;
import java.util.concurrent.*;

public class DataStore {
private Map responseTimes = new ConcurrentHashMap();
private boolean available = false;

public DataStore() {
}

public synchronized void putResponseTime(String key, int val) {
responseTimes.put(key, val);
notifyAll();
}

public synchronized int getResponseTime(String key) {
notifyAll();
int val=-1;
if(responseTimes.containsKey(key)){
val = responseTimes.get(key);
}
return val;
}
}

In here you can get as creative as you want. I'm using the ConcurrentHashMap to store key/val pairs of data which on its own is considered thread safe. I'm not sure if it is redudant code but I chose to make the public methods synchronized aswell, in order to ensure that threads don't get into a spin when trying to access the same. To quote Sun

First, it is not possible for two invocations of synchronized methods on the same object to interleave. When one thread is executing a synchronized method for an object, all other threads that invoke synchronized methods for the same object block (suspend execution) until the first thread is done with the object.

Second, when a synchronized method exits, it automatically establishes a happens-before relationship with any subsequent invocation of a synchronized method for the same object. This guarantees that changes to the state of the object are visible to all threads.

Perhaps someone with a better understanding of Java and threading can explain if this is required given that I'm already using a concurrent collection? If you weren't, then you'd definitely need to consider synchronized methods ...

But right now, using the skeleton provided, you should be able to write thread safe programs, and easily share data between worker threads and a boss thread(s). I'm using this design at the moment to work on a MQ load harness, and hope that it will be available for public use soon. :)

Social tagging:

3 Responses to Sharing data safely between Java threads

  1. gleery

    Nice~, it helps

  2. Aakash

    nice start!