elfar
elfar

Reputation: 481

how to unit test a synchronized method?

Say I have such a method:

synchronized void incrementIndex() {
      index++;
}

I want to unit test this method to see whether index's final value is set correctly if multiple threads simultaneously attempt to increment index. Assuming I don't know about the "synchronized" keyword in the method's declaration (and I only know the contract of the method), how can I do the test?

p.s. I am using Mockito for writing test cases if it helps.

Upvotes: 13

Views: 28170

Answers (3)

LeffeBrune
LeffeBrune

Reputation: 3477

You could test this by having multiple threads execute the method and then asserting that the result is what you would expect. I have my doubts about how effective and reliable this would be. Multithreaded code is notoriously difficult to test and it mostly comes down to careful design. I would definitely recommend adding tests that assert that the methods you expect to by synchronized actually have the synchronized modifier. See an example of both approaches below:

import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.CoreMatchers.not;
import static org.junit.Assert.assertThat;

import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

import org.junit.Test;

public class SyncTest {
  private final static int NUM_THREADS = 10;
  private final static int NUM_ITERATIONS = 1000;

  @Test
  public void testSynchronized() throws InterruptedException {
    // This test will likely perform differently on different platforms.
    ExecutorService executor = Executors.newFixedThreadPool(NUM_THREADS);
    final Counter sync = new Counter();
    final Counter notSync = new Counter();

    for (int i = 0; i < NUM_THREADS; i++) {
      executor.submit(new Runnable() {
        @Override
        public void run() {
          for (int i = 0; i < NUM_ITERATIONS; i++) {
            sync.incSync();
            notSync.inc();
          }
        }
      });
    }

    executor.shutdown();
    executor.awaitTermination(5, TimeUnit.SECONDS);
    assertThat(sync.getValue(), is(NUM_THREADS * NUM_ITERATIONS));
    assertThat(notSync.getValue(), is(not(NUM_THREADS * NUM_ITERATIONS)));
  }

  @Test
  public void methodIncSyncHasSynchronizedModifier() throws Exception {
    Method m = Counter.class.getMethod("incSync");
    assertThat(Modifier.isSynchronized(m.getModifiers()), is(true)); 
  }

  private static class Counter {
    private int value = 0;

    public synchronized void incSync() {
      value++;
    }

    public void inc() {
      value++;
    }

    public int getValue() {
      return value;
    }
  }
}

Upvotes: 9

johnlon
johnlon

Reputation: 177

Is. i++. Atomic?

No

So sync is justified if you care about the correctness of your program.

But testing is hard.

Visual inspection tells us that the nonatomic increment ooperation is protected and made atomic and all is well as far as we know, but we're don't know anything about the state of the rest of the system.

It is possible to test that a function is sychronised only by its side effects. There is a testable pattern that orgs the code such that you dependency inject the sync rather than using the Jave intrinsic, but if all there is is your original question then I would rely on visual inspection and obvious correctness.

Upvotes: -2

solimant
solimant

Reputation: 891

CandiedOrange is correct in his comment to your question. In other words, and given the method you mentioned, you should not worry about threadA calling the method at the same moment threadB is since both calls are writing to index. Had it been something like:

void incrementIndex() {
     index++;
     System.out.println(index); // threadB might have written to index
                                // before this statement is executed in threadA
}

threaA calls the method, increments index in the first statement, then attempts to read the value of index in the second statement, by which time threadB might have already made the call to the method and incremented index before threadA reads it and prints it. This is where synchronized is necessary to avoid such a situation.

Now, if you still want to test synchronization, and you have access to the method code (or maybe you can do a similar prototype), you may consider something like the following that illustrates how multithreading behaves with synchronized methods:

public void theMethod(long value, String caller) {
    System.out.println("thread" + caller + " is calling...");
    System.out.println("thread" + caller + " is going to sleep...");

    try {
        Thread.sleep(2000);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }

    System.out.println("thread" + caller + " woke up!");
}

This should output:

threadA is calling...
threadA is going to sleep...
threadA woke up!
threadB is calling...
threadB is going to sleep...
threadB woke up!

Without the synchronized keyword, the output be:

threadA is calling...
threadA is going to sleep...
threadB is calling...
threadB is going to sleep...
threadA woke up!
threadB woke up!

Upvotes: 4

Related Questions