Comparison between locks in Java

Keywords: Programming Java JDK less jvm

The difference between synchronized and java.util.concurrent.lock.Lock

  • The implementation level is different. synchronized is a Java keyword, which implements locking and releasing at the JVM level; Lock is an interface, which implements locking and releasing at the code level
  • Whether the Lock is released automatically. synchronized releases the Lock automatically when the thread code finishes executing or an exception occurs; Lock does not release the Lock automatically and needs to explicitly release the Lock in the {} finally code block
  • Whether to wait all the time. synchronized will cause the thread to fail to get the Lock and wait; Lock can set the timeout for some time when trying to get the Lock or failed to get the Lock
  • Whether the Lock is obtained successfully is known. synchronized can't know whether the Lock is obtained successfully or not; Lock can obtain the Lock success through tryLock
  • Functional complexity. synchronized Lock can be reentrant, non interruptible, and non fair; Lock can be reentrant, judgmental, fair and unfair, and subdivided read-write Lock can improve efficiency

 

The difference between java.util.concurrent.lock.Lock and java.util.concurrent.lock.ReadWriteLock

  • ReadWriteLock defines the interface for acquiring read lock and write lock. The read lock is not mutually exclusive. It is very suitable for scenarios with more reading and less writing.

 

Applicable scenario

  • At the beginning of JDK 1.6, we optimized the shackles of synchronized mode, added biased lock, lightweight lock and lock upgrade mechanism, and greatly improved the performance. Performance similar to ReentrantLock
  • Consider using ReadWriteLock when reading more and writing less

 

synchronized, ReentrantLock, ReentrantReadWriteLock start 990 thread read shared variables, 10 thread write shared variables

package constxiong.concurrency.a020;

import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

/**
 * JDK 1.8 Test of middle lock performance
 * @author ConstXiong
 */
public class TestLockPerformance {
	
	public static Object obj = new Object();//For synchronized lock acquisition
	
	public static Lock lock = new ReentrantLock();//Reentrant lock
	
	public static ReadWriteLock readWriteLock = new ReentrantReadWriteLock();//Read write lock
	
	public static final int READ = 0;
	
	public static final int WRITE = 1;
	
	// uuid, a random string
	public static String uuid = UUID.randomUUID().toString();

	public static void main(String[] args) throws InterruptedException {
//		testSynchronized(1000);
		
		testReentrantLock(1000);
		
//		testReadWriteLock(1000);
	}
	
	public static void testSynchronized(int threadNum) throws InterruptedException {
		long t1 = System.currentTimeMillis();
		List<Thread> tList = new ArrayList<Thread>();
		//Start threadNum - round up (0.01 * threadNum) threads to read uuid, round up (0.01 * threadNum) threads to write uuid
		for (int i = 0; i < threadNum; i++) {
			Thread t;
			if (i % 100 == 0) {
				t = new Thread(new WorkerSynchronized(WRITE));
			} else {
				t = new Thread(new WorkerSynchronized(READ));
			}
			t.start();//Startup thread
			tList.add(t);
		}
		
		for (Thread t : tList) {
			t.join();
		}
		
		long t2 = System.currentTimeMillis();
		System.out.println("testSynchronized Time consuming:" + (t2 - t1));
	}
	
	public static void testReentrantLock(int threadNum) throws InterruptedException {
		long t1 = System.currentTimeMillis();
		List<Thread> tList = new ArrayList<Thread>();
		//Start threadNum - round up (0.01 * threadNum) threads to read uuid, round up (0.01 * threadNum) threads to write uuid
		for (int i = 0; i < threadNum; i++) {
			Thread t;
			if (i % 100 == 0) {
				t = new Thread(new WorkerReentrantLock(WRITE));
			} else {
				t = new Thread(new WorkerReentrantLock(READ));
			}
			t.start();//Startup thread
			tList.add(t);
		}
		
		for (Thread t : tList) {
			t.join();
		}
		
		long t2 = System.currentTimeMillis();
		System.out.println("testReentrantLock Time consuming:" + (t2 - t1));
	}
	
	public static void testReadWriteLock(int threadNUm) throws InterruptedException {
		long t1 = System.currentTimeMillis();
		List<Thread> tList = new ArrayList<Thread>();
		//Start threadNum - round up (0.01 * threadNum) threads to read uuid, round up (0.01 * threadNum) threads to write uuid
		for (int i = 0; i < threadNUm; i++) {
			Thread t;
			if (i % 100 == 0) {
				t = new Thread(new WorkerReadWriteLock(WRITE));
			} else {
				t = new Thread(new WorkerReadWriteLock(READ));
			}
			t.start();//Startup thread
			tList.add(t);
		}
		
		for (Thread t : tList) {
			t.join();
		}
		
		long t2 = System.currentTimeMillis();
		System.out.println("testReadWriteLock Time consuming:" + (t2 - t1));
	}
	
}

//Worker thread, lock with synchronized keyword
class WorkerSynchronized implements Runnable {
	//0-read;1-write
	private int type;
	
	WorkerSynchronized(int type) {
		this.type = type;
	}
	
	//Lock the TestLockPerformance.uuid variable and print it
	private void read() {
		synchronized (TestLockPerformance.obj) {
			//Sleep for 20 ms, simulation task execution time
			try {
				Thread.sleep(20);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			System.out.println(Thread.currentThread().getName() + 
					" read uuid = " +  TestLockPerformance.uuid);
		}
	}
	
	//Lock the TestLockPerformance.uuid variable and print it
	private void write() {
		synchronized (TestLockPerformance.obj) {
			//Sleep for 20 ms, simulation task execution time
			try {
				Thread.sleep(20);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			TestLockPerformance.uuid = UUID.randomUUID().toString();
			System.out.println(Thread.currentThread().getName() + 
					" write uuid = " +  TestLockPerformance.uuid);
		}
	}
	
	@Override
	public void run() {
		//type = 0, thread reads TestLockPerformance.uuid variable
		if (type == 0) {
			read();
		//type = 1, the thread generates UUID, and writes the TestLockPerformance.uuid variable
		} else {
			write();
		}
	}
}

//Worker threads, locking with ReentrantLock
class WorkerReentrantLock implements Runnable {
	//0-read;1-write
	private int type;
	
	WorkerReentrantLock(int type) {
		this.type = type;
	}
	
	//Lock the TestLockPerformance.uuid variable and print it
	private void read() {
		TestLockPerformance.lock.lock();
		try {
			//Sleep for 20 ms, simulation task execution time
			try {
				Thread.sleep(20);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			System.out.println(Thread.currentThread().getName() + 
					" read uuid = " +  TestLockPerformance.uuid);
		} finally {
			TestLockPerformance.lock.unlock();
		}
		
	}
	
	//Lock the TestLockPerformance.uuid variable and print it
	private void write() {
		TestLockPerformance.lock.lock();
		try {
			//Sleep for 20 ms, simulation task execution time
			try {
				Thread.sleep(20);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			TestLockPerformance.uuid = UUID.randomUUID().toString();
			System.out.println(Thread.currentThread().getName() + 
					" write uuid = " +  TestLockPerformance.uuid);
		} finally {
			TestLockPerformance.lock.unlock();
		}
	}
	
	@Override
	public void run() {
		//type = 0, thread reads TestLockPerformance.uuid variable
		if (type == 0) {
			read();
		//type = 1, the thread generates UUID, and writes the TestLockPerformance.uuid variable
		} else {
			write();
		}
	}
}


//Worker thread, lock with ReentrantReadWriteLock keyword
class WorkerReadWriteLock implements Runnable {
	//0-read;1-write
	private int type;
	
	WorkerReadWriteLock(int type) {
		this.type = type;
	}
	
	//Lock the TestLockPerformance.uuid variable and print it
	private void read() {
		TestLockPerformance.readWriteLock.readLock().lock();
		try {
			//Sleep for 20 ms, simulation task execution time
			try {
				Thread.sleep(20);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			System.out.println(Thread.currentThread().getName() + 
					" read uuid = " +  TestLockPerformance.uuid);
		} finally {
			TestLockPerformance.readWriteLock.readLock().unlock();
		}
	}
	
	//Lock the TestLockPerformance.uuid variable and print it
	private void write() {
		TestLockPerformance.readWriteLock.writeLock().lock();
		try {
			//Sleep for 20 ms, simulation task execution time
			try {
				Thread.sleep(20);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			TestLockPerformance.uuid = UUID.randomUUID().toString();
			System.out.println(Thread.currentThread().getName() + 
					" write uuid = " +  TestLockPerformance.uuid);
		} finally {
			TestLockPerformance.readWriteLock.writeLock().unlock();
		}
	}
	
	@Override
	public void run() {
		//type = 0, thread reads TestLockPerformance.uuid variable
		if (type == 0) {
			read();
		//type = 1, the thread generates UUID, and writes the TestLockPerformance.uuid variable
		} else {
			write();
		}
	}
}

 

Call test method

testSynchronized(1000);

time consuming

Thread-0 write uuid = b7fb63d7-79cc-4cc0-84ed-5a9cd4de6824
Thread-252 read uuid = b7fb63d7-79cc-4cc0-84ed-5a9cd4de6824
Thread-251 read uuid = b7fb63d7-79cc-4cc0-84ed-5a9cd4de6824
.
.
.
Thread-255 read uuid = d666bfe6-dc71-4df2-882a-d530a59d7e92
Thread-254 read uuid = d666bfe6-dc71-4df2-882a-d530a59d7e92
Thread-253 read uuid = d666bfe6-dc71-4df2-882a-d530a59d7e92
testSynchronized Time: 22991

 

Call test method

testReentrantLock(1000);

time consuming

Thread-0 write uuid = 4352eb13-d284-47ec-8caa-fc81d91d08e1
Thread-1 read uuid = 4352eb13-d284-47ec-8caa-fc81d91d08e1
Thread-485 read uuid = 4352eb13-d284-47ec-8caa-fc81d91d08e1
.
.
.
Thread-997 read uuid = 9d7f0a78-5eb7-4506-9e98-e8e9a7a717a5
Thread-998 read uuid = 9d7f0a78-5eb7-4506-9e98-e8e9a7a717a5
Thread-999 read uuid = 9d7f0a78-5eb7-4506-9e98-e8e9a7a717a5
testReentrantLock Time: 22935

 

Call test method

testReadWriteLock(1000);

time consuming

Thread-0 write uuid = 81c13f80-fb19-4b27-9d21-2e99f8c8acbd
Thread-277 read uuid = 81c13f80-fb19-4b27-9d21-2e99f8c8acbd
Thread-278 read uuid = 81c13f80-fb19-4b27-9d21-2e99f8c8acbd
.
.
.
Thread-975 read uuid = 35be0359-1973-4a4f-85b7-918053d841f7
Thread-971 read uuid = 35be0359-1973-4a4f-85b7-918053d841f7
Thread-964 read uuid = 35be0359-1973-4a4f-85b7-918053d841f7
testReadWriteLock Time consuming: 543

 

It can be seen from the time-consuming test that using synchronized and ReentrantLock takes approximately the same time; however, because 990 threads read and 10 threads write, using ReentrantReadWriteLock takes 543 milliseconds.

 

PS: JDK and the tools in the contract. There are many tools suitable for specific scenarios. We will continue to explore later.

       

Java interview question summary, there is always a stuck you!

Posted by Rohlan on Wed, 16 Oct 2019 04:55:49 -0700