Thread Safety Strategies
Published:
Recently I was asked to look into a production issue in a payment application. After going through hours of debugging we could zero into the cause of the problem. Not surprisingly it was due concurrent requests from different users arriving at our server in same instant. Thankfully the fix was quick in this case, but this let me to think how important it is for todays developers to know about Thread Safety and how their code will behave in case of multi-threaded environment
Why should we worry about Thread Safe Classes
Where threads were more esoteric, thread safety was considered as an advanced topic; now mainstream developers must be aware of thread-safety issues Brian Goetz
Invariably the code you are writing will most likely be executed in a multi-core environment and you can no longer assume that it is only one thread.
A piece of code that is not thread safe might appear to work, passing its tests and performing well for years, but it is still broken and may fail at any moment
One of the critical reason to be aware of Thread Safety is there are no definative tools to test for Thread Safety. It is vey difficult to write JUnit for concurrent execution of piece of code
Attemps to Test for Thread Safety In this article I am refering two ways which we can write some JUnits to test for concurrency
Weaver
Weaver is a framework for writing multi-threaded Unit Tests in Java. Let’s see an example of the same.
The below class is an example of Thread Un-Safe class.
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class EmployeeList {
private List employee = Collections.synchronizedList(new ArrayList());
public boolean putIfAbsent(String employeeName) {
boolean absent = false;
absent = !employee.contains(employeeName);
if (absent) {
employee.add(employeeName);
}
return absent;
}
public int size() {
return employee.size();
}
}
With some bad timing we can have two threads finding list does not contain the employee name and so both the threads would add the element in the list Below is a JUnit using Weaver to test it
import org.junit.Test;
import com.google.testing.threadtester.AnnotatedTestRunner;
import com.google.testing.threadtester.ThreadedAfter;
import com.google.testing.threadtester.ThreadedBefore;
import com.google.testing.threadtester.ThreadedMain;
import com.google.testing.threadtester.ThreadedSecondary;
import static org.junit.Assert.*;
public class EmployeeListMultiThreadedTest {
EmployeeList list;
@Test
public void testPutIfAbsent() {
AnnotatedTestRunner runner = new AnnotatedTestRunner();
// Run all Weaver tests in this class, using EmployeeList as the Class Under
// Test.
runner.runTests(this.getClass(), EmployeeList.class);
}
@ThreadedBefore
public void before() {
list = new EmployeeList();
}
@ThreadedMain
public void mainThread() {
list.putIfAbsent("Dhananjay");
}
@ThreadedSecondary
public void secondThread() {
list.putIfAbsent("Dhananjay");
}
@ThreadedAfter
public void after() {
assertEquals(1, list.size());
}
}
If you run the above Unit test, it would fail with an Error as size would be 2.
Explanation of how Weaver works in the above test is given here Weaver Explained.
I found this to be very useful to quickly write JUnits to verify how our classes would behave in case of multi-threaded environment
vmlens
vmlens enables you to unit test your multithreaded java code. The following exampl shows how you can use vmlens in your JUnits
public class Counter {
private int count = 0;
public void addOne() {
count++;
}
public int getCount() {
return count;
}
}
This is classic Thread Unsafe code. Below is JUnit which we can use to test the same
import static org.junit.Assert.assertEquals;
import org.junit.After;
import org.junit.Test;
import org.junit.runner.RunWith;
import com.anarsoft.vmlens.concurrent.junit.ConcurrentTestRunner;
@RunWith(ConcurrentTestRunner.class)
public class CounterTest {
private Counter counter = new Counter();
@Test
public void addOne() {
counter.addOne();
}
@After
public void testCount() {
assertEquals("4 Threads running addOne in parallel should lead to 4" , 4 , counter.getCount());
}
}
By using the RunWith annotation the JUnit test is run by a special ConcurrentTestRunner. This test runner runs the method annotated with “Test” parallel in 4 threads. After that it executes the methods marked with the annotation After” in the main thread. If you run the test case very often, you will sometimes see an assertion failed exception.
Guidelines for Thread Safety
3 common concurrency mechanisms generally used in combination with each other to safely access data from multiple threads
- Dynamic Exclusion Through Implicit Locks
- Structural Exclusion Through Confinement
- Immutability
I will try to explain more on these guidelines in following articles.
Leave a Comment
Your email address will not be published. Required fields are marked *