java Concurrent Programming
We often encounter such a scenario, that is, many people access a resource of the system at the same time, which is a high concurrency situation, which is equivalent to the problem of multi-threaded resource sharing. If it is not handled, it will lead to logic errors in the final result. The java package provides a variety of locks to solve this problem.
Classification of locks
Optimistic lock / pessimistic lock
Optimistic lock means that every time you get the data, you think that others will not modify it, so you will not lock it. However, when updating, you will judge whether others have updated the data during this period. You can use mechanisms such as version number, such as cas operation.
Pessimistic locks believe that concurrent operations on the same data will be modified, even if they are not modified. Therefore, pessimistic locking takes the form of locking for concurrent operations of the same data. Pessimists believe that there will be problems with concurrent operations without locks.
Exclusive lock / shared lock
Exclusive lock means that the lock can only be held by one thread at a time. Shared lock means that the lock can be held by multiple threads. A read lock is a shared lock, and a write lock is an exclusive lock and a mutually exclusive lock.
Reentrant lock
Reentrant lock, also known as recursive lock, means that when the same thread obtains a lock in the outer method, it will automatically obtain the lock when entering the inner method.
synchronized void setA() throws Exception{ Thread.sleep(1000); setB(); } synchronized void setB() throws Exception{ Thread.sleep(1000); }
Fair lock / unfair lock
Fair lock means that the waiting thread obtains the lock according to the queuing order, and non fair lock means that the waiting thread obtains the lock at random.
Bias lock / lightweight lock / heavyweight lock
These three locks refer to the state of the lock and are for Synchronized. In Java 5, the mechanism of lock upgrade is introduced to realize efficient Synchronized. The status of these three locks is indicated by the fields in the object header of the object monitor.
Biased lock means that a piece of synchronous code has been accessed by a thread, and the thread will automatically obtain the lock. Reduce the cost of obtaining locks.
Lightweight lock means that when a lock is a biased lock and is accessed by another thread, the biased lock will be upgraded to a lightweight lock. Other threads will try to obtain the lock in the form of spin without blocking and improving performance.
Heavyweight lock means that when the lock is a lightweight lock, although another thread spins, the spin will not continue all the time. When it spins a certain number of times, it will enter blocking before it has obtained the lock, and the lock will expand into a heavyweight lock. The heavyweight lock will block the thread he applies for, including the switching between kernel state and user state caused by system calls, which will reduce the performance.
Spin lock
In Java, spin lock means that the thread trying to obtain the lock will not block immediately, but will try to obtain the lock in a loop. This has the advantage of reducing the consumption of thread context switching, but the disadvantage is that the loop will consume CPU.
CAS
What is CAS
CAS is an atomic operation of computer memory. It is the implementation of optimistic lock. It is mainly divided into two steps: comparison and exchange. First, compare the memory value with the expected value. If it is the same, update it to a new value. Lock lock and related atomic reference classes are implemented with it.
ABA problems caused by CAS
The so-called ABA problem refers to a thread changing the start value to another value, and finally to the start value, that is, a - > b - > A. another thread comes in and finds that the value has not changed, so it carries out CAS operation. The solution is to record a version number for each CAS operation. Before each operation, compare the version numbers, and operate as long as the version numbers are the same.
public class TestABA { public static void main(String[] args) { AtomicReference<Integer> atomicReference = new AtomicReference<>(100); AtomicStampedReference<Integer> atomicStampedReference = new AtomicStampedReference(100,1); System.out.println("ABA Problem generation============="); new Thread(()->{ atomicReference.compareAndSet(100,101); atomicReference.compareAndSet(101,100); },"t1").start(); new Thread(()->{ try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } atomicReference.compareAndSet(100,2009); System.out.println("Modification result:"+atomicReference.get()); },"t2").start(); try { Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("ABA Problem solving============================"); new Thread(()->{ System.out.println(Thread.currentThread().getName()+"First version number of:"+atomicStampedReference.getStamp()); try { //Make sure t4 gets the same version number as t3 Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } atomicStampedReference.compareAndSet(100,101,atomicStampedReference.getStamp(),atomicStampedReference.getStamp()+1); System.out.println(Thread.currentThread().getName()+"Version No. 2 of:"+atomicStampedReference.getStamp()); atomicStampedReference.compareAndSet(101,100,atomicStampedReference.getStamp(),atomicStampedReference.getStamp()+1); System.out.println(Thread.currentThread().getName()+"3rd edition of:"+atomicStampedReference.getStamp()); },"t3").start(); new Thread(()->{ int timp = atomicStampedReference.getStamp(); System.out.println(Thread.currentThread().getName()+"First version number of:"+timp); try { //Ensure t3 modification is completed Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } boolean result = atomicStampedReference.compareAndSet(100,2019,timp,timp+1); System.out.println(Thread.currentThread().getName()+"Modified successfully:"+result); System.out.println("The current version number is"+atomicStampedReference.getStamp()); System.out.println("Current latest value:"+atomicStampedReference.getReference()); },"t4").start(); } }
Comparison between synchronized and lock
synchronized and lock, as the basic locks in java, have different characteristics
- synchronized is a keyword based on the jvm level, while lock is a class based on cas operations.
- synchronized does not need to release the lock manually, and the lock needs to be released manually in finally.
- Although synchronized is optimized, there is a process from biased lock to lightweight lock and then to heavyweight lock, and finally heavyweight lock will be formed. Heavyweight lock will bring performance overhead, and lock is a lightweight lock.
- lock has more accurate thread wake-up than synchronized.
- Both lock and synchronized are non fair locks by default. Lock can be set as fair locks, and both have reentry.
LOCK lock and its related classes
ReentrantLock
ReentrantLock, like synchronized, has poor performance because it is exclusive to other threads in terms of reading and writing.
ReentrantReadWriteLock
ReentrantReadWriteLock is a read-write lock. It can generate read locks and write locks.
class MyCache{ Map map = new HashMap(); ReadWriteLock readWriteLock = new ReentrantReadWriteLock(); public void put(String key,String value){ try { readWriteLock.writeLock().lock(); System.out.println(Thread.currentThread().getName()+"The thread is writing data"); try { TimeUnit.MICROSECONDS.sleep(300); } catch (InterruptedException e) { e.printStackTrace(); } map.put(key,value); System.out.println(Thread.currentThread().getName()+"Thread write data complete"); }catch (Exception e){ }finally { readWriteLock.writeLock().unlock(); } } public void get(String key){ try { readWriteLock.readLock().lock(); System.out.println(Thread.currentThread().getName()+"The thread is reading data"); try { TimeUnit.MICROSECONDS.sleep(300); } catch (InterruptedException e) { e.printStackTrace(); } map.get(key); System.out.println(Thread.currentThread().getName()+"Thread reading data completed"); }catch (Exception e){ }finally { readWriteLock.readLock().unlock(); } } } public class TestLock { public static void main(String[] args) { MyCache myCache = new MyCache(); for (int i = 0; i <5 ; i++) { final int itm = i; new Thread(()->{ myCache.get(itm+""); },String.valueOf(i)).start(); } for (int i = 0; i <5 ; i++) { final int itm = i; new Thread(()->{ myCache.put(itm+"",itm+"); },String.valueOf(i)).start(); } } }
lock's precise wake-up
The so-called accurate wake-up refers to the task number completed by one thread, which can be specified to be executed by other threads.
class Resoruce2{ private Lock lock = new ReentrantLock(); private int num = 1; private Condition condition1 = lock.newCondition(); private Condition condition2 = lock.newCondition(); private Condition condition3 = lock.newCondition(); public void print5(){ lock.lock(); try { if(num!=1){ condition1.await(); } for (int i = 1; i <6 ; i++) { System.out.println(Thread.currentThread().getName()+" "+i); } num=2; condition2.signal(); }catch (Exception e){ }finally { lock.unlock(); } } public void print10(){ lock.lock(); try { if(num!=2){ condition2.await(); } for (int i = 1; i <11 ; i++) { System.out.println(Thread.currentThread().getName()+" "+i); } num=3; condition3.signal(); }catch (Exception e){ }finally { lock.unlock(); } } public void print15(){ lock.lock(); try { if(num!=3){ condition3.await(); } for (int i = 1; i <16 ; i++) { System.out.println(Thread.currentThread().getName()+" "+i); } num=1; condition1.signal(); }catch (Exception e){ }finally { lock.unlock(); } } } public class TestLockNotify { public static void main(String[] args) { Resoruce2 resoruce2 = new Resoruce2(); new Thread(()->{ for (int i = 0; i <10 ; i++) { resoruce2.print5(); } },"AA").start(); new Thread(()->{ for (int i = 0; i <10 ; i++) { resoruce2.print10(); } },"BB").start(); new Thread(()->{ for (int i = 0; i <10 ; i++) { resoruce2.print15(); } },"CC").start(); } }
Usage of CountDownLatch
The function of CountDownLatch is to control whether a thread can execute down. The thread can execute down only after other threads have finished executing. For example, the monitor can't close the door until the whole class has gone.
public class TestCoutLaunch { public static void main(String[] args) throws InterruptedException { CountDownLatch countDownLatch = new CountDownLatch(6); for (int i = 0; i <6 ; i++) { final int num = i; new Thread(()->{ System.out.println("The first"+num+"A classmate went out"); countDownLatch.countDown(); },String.valueOf(i)).start(); } countDownLatch.await(); System.out.println("The monitor locked the door====================="); } }
Usage of CyclicBarrier
The usage of CyclicBarrier is similar to that of CountDownLatch, except that it is accumulated each time after the execution of other threads, while CountDownLatch is subtracted. In a word, CyclicBarrier does not meet until all people arrive.
public class TestCyBar { public static void main(String[] args) { CyclicBarrier cyclicBarrier = new CyclicBarrier(6,()->{ System.out.println("Everyone arrived for the meeting"); }); for (int i = 1; i <7 ; i++) { int num = i; new Thread(()->{ System.out.println("The first"+num+"Here comes a man"); try { cyclicBarrier.await(); } catch (InterruptedException e) { e.printStackTrace(); } catch (BrokenBarrierException e) { e.printStackTrace(); } }).start(); } } }

Usage of Semaphore
If lock and synchronized lock a single resource, Semaphore is equivalent to locking multiple resources, such as grabbing parking spaces. If there are only three parking spaces, only three threads must grab parking spaces each time. Only when the resources are released can other threads compete for parking spaces.
public class TestSemaphore { public static void main(String[] args) { //Represents three resources, such as parking spaces Semaphore semaphore = new Semaphore(3); for (int i = 1; i <7 ; i++) { final int num = i; new Thread(()->{ try { semaphore.acquire(); System.out.println("The first"+num+"A car grabbed the parking space"); Thread.sleep(3000); System.out.println("The first"+num+"The car left the parking space"); } catch (InterruptedException e) { e.printStackTrace(); }finally { semaphore.release(); } },String.valueOf(i)).start(); } } }
Deadlock problem
Deadlock means that two threads have acquired their own locks, but need each other's locks. The two sides do not give in to each other, resulting in the failure of downward execution of the program. You can use jps and jstack commands for troubleshooting.
class MyThread implements Runnable{ private String lockA; private String lockB; public MyThread(String lockA, String lockB) { this.lockA = lockA; this.lockB = lockB; } @Override public void run() { synchronized (lockA){ System.out.println("I'm a thread"+Thread.currentThread().getName()+"==="+"hold"+lockA+"Lock, trying to get"+lockB+"lock"); try { Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } synchronized (lockB){ System.out.println("I'm a thread"+Thread.currentThread().getName()+"==="+"hold"+lockB+"Lock, trying to get"+lockA+"lock"); } } } } public class TestDeadLock { public static void main(String[] args) { String lockA="lockA"; String lockB="lockB"; new Thread(new MyThread(lockA,lockB),"AA").start(); new Thread(new MyThread(lockB,lockA),"BB").start(); } }
Partial reference:
https://www.cnblogs.com/hustzzl/p/9343797.html