Sychronized blocks allow you to choose which monitor you use as the lock
A synchronized block looks like:
synchronized ( this ) {
// add code that you only want one thread to run at a time
}
In this example "this" is termed the "monitor" and refers to the instance of the class this code is in.
An instance of something else can be used as the monitor:
Blah blah = new Blah();
synchronized ( blah ) {
// add code that you only want one thread to run at a time
}
In our example below we dynamically dig out the monitor we want to use from a map. Different threads can use different monitors. See the code for the example.
To get the code for this example:
git clone https://github.com/spotadev/java-examples.git
In both src/main/java and src/test/java navigate to this package:
com.javaspeak.java_examples.concurrency.synchronizedblock
You can run the testng unit test using a testng plugin for your IDE or you can run the main method of:
SynchronizedBlockExampleTest
package com.javaspeak.java_examples.concurrency.synchronizedmethod;
import java.util.concurrent.CountDownLatch;
/**
* @author John Dickerson - 25 Nov 2022
*/
public interface SynchronizedMethodExample {
public void doSomething( CountDownLatch latch );
}
package com.javaspeak.java_examples.concurrency.synchronizedmethod;
import java.util.concurrent.CountDownLatch;
/**
* @author John Dickerson - 25 Nov 2022
*/
public class SynchronizedMethodExampleImpl implements SynchronizedMethodExample {
// See SynchronizedMethodExampleTest for prove that only one thread can access a synchronized method
// at a time. Also See SynchronizedMethodExampleTest for explanation of what the CountDownLatch is
// about.
@Override
public synchronized void doSomething( CountDownLatch latch ) {
try {
Thread.sleep( 1000 );
latch.countDown();
}
catch ( InterruptedException e ) {
// do nothing
}
}
}
package com.javaspeak.java_examples.concurrency.synchronizedblock;
import java.util.concurrent.CountDownLatch;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.testng.Assert;
import org.testng.TestListenerAdapter;
import org.testng.TestNG;
import org.testng.annotations.Test;
import com.javaspeak.java_examples.concurrency.synchronizedmethod.SynchronizedMethodExampleTest;
/**
* @author John Dickerson - 27 Nov 2022
*/
public class SynchronizedBlockExampleTest {
private Logger logger = LoggerFactory.getLogger( SynchronizedBlockExampleTest.class );
@Test
public void doTest() throws InterruptedException {
SynchronizedBlockExample example = new SychnronizedBlocExampleImpl();
CountDownLatch latch = new CountDownLatch( 6 );
Thread[] threads = new Thread[6];
final Integer[] keys = new Integer[] { 1, 1, 2, 2, 3, 3 };
for ( int i = 0; i < 6; i++ ) {
final Integer key = keys[i];
threads[i] = new Thread() {
public void run() {
example.doSomething( key, latch );
}
};
}
long start = System.nanoTime();
// starting all the threads
for ( Thread thread : threads ) {
thread.start();
}
latch.await();
long end = System.nanoTime();
// The start value of the first nanoTime() is arbitrary - it is not based on an absolute
// value like Calendar. So what we care about is the difference in nanoTime between start
// and end. There are 1000 million nano seconds in 1 second.
long diffNano = end - start;
long diffSeconds = diffNano / 1000000000;
logger.info( "diffSeconds: " + diffSeconds );
// We have 6 threads but only 3 different monitors. A monitor is the instance reference
// used in the synchronized block. We have 2 threads per monitor so the time taken is 2
// seconds.
Assert.assertTrue( diffSeconds == 2 );
}
public static void main( String[] args ) {
TestListenerAdapter tla = new TestListenerAdapter();
TestNG testng = new TestNG();
testng.setTestClasses( new Class[] { SynchronizedBlockExampleTest.class } );
testng.addListener( tla );
testng.run();
}
}
Back: Concurrency
Page Author: JD