Over a million developers have joined DZone.

Performance Comparison of Multithreading in Java

· Performance Zone

Download Forrester’s “Vendor Landscape, Application Performance Management” report that examines the evolving role of APM as a key driver of customer satisfaction and business success, brought to you in partnership with BMC.

There are different techniques for multithreading in Java. One can parallelize  a piece of code in Java either with synchronize keywords, locks or atomic variables. This post will compare performances of using synchronized keyword, ReentrantLock, getAndIncrement() and performing continuous trials of get() and compareAndSet() calls. Different types of Matrix classes are created for performance testing and a plain one also included. For comparison, all cells incremented 100 times for different sizes of matrices, with different types of synchronizations, thread counts and pool sizes at a computer which has Intel Core I7 (has 8 cores – 4 of them are real), Ubuntu 14.04 LTS and Java 1.7.0_60.

This is the plain matrix class of performance test:

/**
* Plain matrix without synchronization.
*/
public class Matrix {
private int rows;
private int cols;
private int[][] array;
/**
* Matrix constructor.
*
* @param rows number of rows
* @param cols number of columns
*/
public Matrix(int rows, int cols) {
this.rows = rows;
this.cols = cols;
array = new int[rows][rows];
}
/**
* Increments all matrix cells.
*/
public void increment() {
for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) {
array[i][j]++;
}
}
}
/**
* Returns a string representation of the object which shows row sums of each row.
*
* @return a string representation of the object.
*/
@Override
public String toString() {
StringBuffer s = new StringBuffer();
int rowSum;
for (int i = 0; i < rows; i++) {
rowSum = 0;
for (int j = 0; j < cols; j++) {
rowSum += array[i][j];
}
s.append(rowSum);
s.append(" ");
}
return s.toString();
}
}

For other ones, increment methods of them are listed due to remaining parts are same for each matrix types.

Synchronized matrix:

public void increment() {
for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) {
synchronized (this) {
array[i][j]++;
}
}
}
}

Lock matrix:

public void increment() {
for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) {
lock.lock();
try {
array[i][j]++;
} finally {
lock.unlock();
}
}
}
}

Atomic getAndIncrement matrix:

public void increment() {
for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) {
array[i][j].getAndIncrement();
}
}
}

Continuous trials of get() and compareAndSet() matrix:

public void increment() {
for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) {
for (; ; ) {
int current = array[i][j].get();
int next = current + 1;
if (array[i][j].compareAndSet(current, next)) {
break;
}
}
}
}
}

Also worker classes are created for each matrix. Here is the worker class of plain one:

/**
* Worker for plain matrix without synchronization.
*
* @author Furkan KAMACI
* @see Matrix
*/
public class PlainMatrixWorker extends Matrix implements Runnable {
private AtomicInteger incrementCount = new AtomicInteger(WorkerDefaults.INCREMENT_COUNT);
/**
* Worker constructor.
*
* @param rows number of rows
* @param cols number of columns
*/
public PlainMatrixWorker(int rows, int cols) {
super(rows, cols);
}
/**
* Increments matrix up to a maximum number.
*
* @see WorkerDefaults
*/
@Override
public void run() {
while (incrementCount.getAndDecrement() > 0) {
increment();
}
}
}

For a correct comparison, all tests are replied 20 times by default. Average and standard errors calculated for each result. Due to there are many dimensions at test set (matrix type, matrix size, pool size, thread count and elapsed time) some features are shown as aggregated at charts. These are the results:

For pool size 2 and thread count 2:

Pool Size 2 - Thread Count 2

For pool size 4 and thread count 4:

Pool Size 4 - Thread Count 4

For pool size 6 and thread count 6:

Pool Size 6 - Thread Count 6

For pool size 8 and thread count 8:

Pool Size 8 - Thread Count 8

For pool size 10 and thread count 10:

Pool Size 10 - Thread Count 10

For pool size 12 and thread count 12:

Pool Size 12 - Thread Count 12

Conclusion:

It can be easily seen that plain version is run fastest. However it does not produce correct results as expected. Worse performance is seen with synchronized blocks (when synchronization is done with “this”). Locks are slightly better than synchronized blocks. However, atomic variables are prominently better from all of them. When atomic getAndIncrement and continous trials of get() and compareAndSet() calls compared it’s shown that their performances are same. Reason behind it can easily be understood when source code of Java is checked:

/**
* Atomically increments by one the current value.
*
* @return the previous value
*/
public final int getAndIncrement() {
for (;;) {
int current = get();
int next = current + 1;
if (compareAndSet(current, next))
return current;
}
}

It can be seen that getAndIncrement is implemented with continuous trials of get() and compareAndSet() within Java (version 1.7) source code. On the other hand when other results are checked the effect of pool size can be seen. When a pool size is used which is less than actual thread counts a performance performance issue will occur.

So, performance comparison of multithreading in Java shows that when a piece of code is decided to be synchronized and performance is an issue, and if such kind of threads will be used as like in the test, one should try to use Atomic variables. Other choices should be locks or synchronized blocks. Also it does not mean that synchronized blocks are always better than locks due to effect of JIT compiler and running a piece of code several times or not.

Source code for performance comparison of multithreading in Java can be downloaded from here: https://github.com/kamaci/performance



See Forrester’s Report, “Vendor Landscape, Application Performance Management” to identify the right vendor to help IT deliver better service at a lower cost, brought to you in partnership with BMC.

Topics:

Published at DZone with permission of Furkan Kamaci, DZone MVB. See the original article here.

Opinions expressed by DZone contributors are their own.

The best of DZone straight to your inbox.

SEE AN EXAMPLE
Please provide a valid email address.

Thanks for subscribing!

Awesome! Check your inbox to verify your email so you can start receiving the latest in tech news and resources.
Subscribe

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}