How do locks work? What are the precautions?

Common locks in Java are

  • synchronized
  • Reentrant lock java.util.concurrent.lock.ReentrantLock
  • Repeatable read-write lock java.util.concurrent.lock.ReentrantReadWriteLock

 

There are three uses of synchronized

  • Modify the common method, execute the method code, and obtain the lock of this of the object itself
package constxiong.concurrency.a18;

import java.util.ArrayList;
import java.util.List;

/**
 * Test synchronized common methods
 * @author ConstXiong
 * @date 2019-09-19 10:49:46
 */
public class TestSynchronizedNormalMethod {

	private int count = 0;
	
//	private void add1000() {
	private synchronized void add1000() { //Modify the add100 method with synchronized to get the correct value 30000
		for (int i = 0; i <1000; i++) {
			count++;
		}
	}
	
	//Start 30 threads, and add 1000 to the count attribute of TestSynchronized object for each thread
	private void test() throws InterruptedException {
		List<Thread> threads = new ArrayList<Thread>(10);
		
		for (int i = 0; i <30; i++) {
			Thread t =  new Thread(() -> {
				add1000();
			});
			t.start();
			threads.add(t);
		}
		
		//Wait for all threads to finish executing
		for (Thread t : threads) {
			t.join();
		}
		
		//Print the value of count
		System.out.println(count);
	}
	
	public static void main(String[] args) throws InterruptedException {
		//Create TestSynchronizedNormalMethod object and call test method
		new TestSynchronizedNormalMethod().test();
	}
}

 

  • Modify static methods, execute method code, and obtain the lock of class object
package constxiong.concurrency.a18;

import java.util.ArrayList;
import java.util.List;

/**
 * Test synchronized static methods
 * @author ConstXiong
 * @date 2019-09-19 10:49:46
 */
public class TestSynchronizedStaticMethod {

	private static int count = 0;
	
	private static void add1000() {
//	private synchronized static void add1000() {/ / use synchronized to modify the add100 method to get the correct value 30000
		for (int i = 0; i <1000; i++) {
			count++;
		}
	}
	
	public static void main(String[] args) throws InterruptedException {
		
		//Start 30 threads, and add 1000 to the count attribute of TestSynchronized object for each thread
		List<Thread> threads = new ArrayList<Thread>(10);
		
		for (int i = 0; i <30; i++) {
			Thread t =  new Thread(() -> {
				add1000();
			});
			t.start();
			threads.add(t);
		}
		
		//Wait for all threads to finish executing
		for (Thread t : threads) {
			t.join();
		}
		
		//Print the value of count
		System.out.println(count);
	}
}

 

  • Lock the Java object, decorate the code block, and display the Java object lock specified to be acquired
package constxiong.concurrency.a18;

import java.util.ArrayList;
import java.util.List;

/**
 * Test synchronized code block
 * @author ConstXiong
 * @date 2019-09-19 10:49:46
 */
public class TestSynchronizedCodeBlock {

	private int count = 0;
	
	//Locked objects
	private final Object obj = new Object();
	
	private void add1000() {
		
		//To perform the following operation of adding 1000, you need to obtain the lock of obj
		synchronized (obj) {
			for (int i = 0; i <1000; i++) {
				count++;
			}
		}
	}
	
	//Start 30 threads, and add 1000 to the count attribute of TestSynchronized object for each thread
	private void test() throws InterruptedException {
		List<Thread> threads = new ArrayList<Thread>(10);
		
		for (int i = 0; i <30; i++) {
			Thread t =  new Thread(() -> {
				add1000();
			});
			t.start();
			threads.add(t);
		}
		
		//Wait for all threads to finish executing
		for (Thread t : threads) {
			t.join();
		}
		
		//Print the value of count
		System.out.println(count);
	}
	
	public static void main(String[] args) throws InterruptedException {
		//Create TestSynchronizedNormalMethod object and call test method
		new TestSynchronizedCodeBlock().test();
	}
}

 

 

Examples of using reentrant lock java.util.concurrent.lock.ReentrantLock

package constxiong.concurrency.a18;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * Test ReentrantLock 
 * @author ConstXiong
 * @date 2019-09-19 11:26:50
 */
public class TestReentrantLock {

	private int count = 0;
	
	private final Lock lock = new ReentrantLock();
	
	private void add1000() {
		lock.lock();
		try {
			for (int i = 0; i <1000; i++) {
				count++;
			}
		} finally {
			lock.unlock();
		}
	}
	
	//Start 30 threads, and add 1000 to the count attribute of TestSynchronized object for each thread
	private void test() throws InterruptedException {
		List<Thread> threads = new ArrayList<Thread>(10);
		
		for (int i = 0; i <30; i++) {
			Thread t =  new Thread(() -> {
				add1000();
			});
			t.start();
			threads.add(t);
		}
		
		//Wait for all threads to finish executing
		for (Thread t : threads) {
			t.join();
		}
		
		//Print the value of count
		System.out.println(count);
	}
	
	public static void main(String[] args) throws InterruptedException {
		//Create TestReentrantLock object and call test method
		new TestReentrantLock().test();
	}
	
}

 

 

Example of using java.util.concurrent.lock.ReentrantReadWriteLock

package constxiong.concurrency.a18;

import java.util.HashMap;
import java.util.Map;
import java.util.Random;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
 
/**
 * Test reentrant readwritelock
 * @author ConstXiong
 * @date 2019-09-19 11:36:19
 */
public class TestReentrantReadWriteLock {
	
	//map storing key value
	private Map<String, Object> map = new HashMap<String, Object>();
	
	//Read write lock
	private final ReadWriteLock lock = new ReentrantReadWriteLock();
	
	/**
	 * Get value according to key
	 * @param key
	 */
	public Object get(String key) {
		Object value = null;
		lock.readLock().lock();
		try {
			Thread.sleep(50L);
			value = map.get(key);
		} catch (InterruptedException e) {
			e.printStackTrace();
		} finally {
			lock.readLock().unlock();
		}
		return value; 
	}
	
	/**
	 * Set key value
	 * @param key
	 */
	public void set(String key, Object value) {
		lock.writeLock().lock();
		try {
			Thread.sleep(50L);
			map.put(key, value);
		} catch (InterruptedException e) {
			e.printStackTrace();
		} finally {
			lock.writeLock().unlock();
		}
	}
 
	//Test 5 threads to read data and 5 threads to write data
	public static void main(String[] args) {
		//Create a test reentrant read-write lock TestReentrantReadWriteLock object
		TestReentrantReadWriteLock test = new TestReentrantReadWriteLock();
		
		String key = "lock";//key stored in map
		Random r = new Random();//Generate random number as value
		
		for (int i = 0; i <5; i++) {
			//Five threads read the value of key in map
			new Thread(() -> {
				for (int j = 0; j <10; j++) {
					System.out.println(Thread.currentThread().getName() + " read value=" + test.get(key));
				}
			}).start();
			
			//Five threads write the value of key in map
			new Thread(() -> {
				for (int j = 0; j <10; j++) {
					int value = r.nextInt(1000);
					test.set(key, value);
					System.out.println(Thread.currentThread().getName() + " write value=" + value);
				}
			}).start();
		}
	}
	
}

 

Precautions for use of lock

  • When the synchronized code block is decorated, it is better not to lock the basic type of wrapper class. For example, the jvm will cache - 128 ~ 127 Integer objects. Each time you define an Integer object in the following way, you will get the same Integer. If you lock it in different places, it may cause strange performance problems or deadlock
Integer i = 100; 
  • When synchronized decorates code blocks, in order for threads to execute code blocks mutually exclusive, it is necessary to ensure that the same object is locked, which is often ignored in actual programming
  • synchronized does not support attempts to acquire locks, lock timeouts, and fair locks
  • ReentrantLock must remember to call the unlock() method in the finally{} statement block to release the lock, otherwise it may lead to deadlock.
  • ReentrantLock consumes CPU resources due to its high concurrency
  • ReentrantReadWriteLock is suitable for scenarios where there are few write operations and frequent read operations on shared resources. It can be downgraded from write lock to read lock, and cannot be upgraded from read lock to write lock


 

 

All resources are summarized in the public address.


 

Tags: Programming Java Attribute jvm

Posted on Sat, 30 Nov 2019 09:37:53 -0800 by youknowho