JUC concurrent programming -- understanding of various locks
1. Fair lock
Fair lock means that when the lock is available, the thread waiting on the lock for the longest time will obtain the right to use the lock and must come first.
//ReentrantLock(true) is set to fair lock public ReentrantLock(boolean fair) { sync = fair ? new FairSync() : new NonfairSync(); }
Unfair locks are randomly assigned this right of use and can jump the queue. Sometimes, some threads take a long time to execute. If other threads need locks, they need to wait a long time. Therefore, in order to provide efficiency, we generally use unfair locks
//ReentrantLock() is a non fair lock by default public ReentrantLock() { sync = new NonfairSync(); }
2. Reentrant lock
Reentrant lock is also called recursive lock, which means that when the same thread obtains the lock in the outer method, it will automatically obtain the lock in the inner method. Basically, reentrant locks are used in JDK to avoid deadlock.
synchronized reentrant lock
public class Demo01 { public static void main(String[] args) { Phone phone = new Phone(); new Thread(()->{ phone.sms(); },"A").start(); } class Phone{ public synchronized void sms(){ System.out.println(Thread.currentThread().getName()+"=> sms"); call();//Inner layer method } //Lock of inner method public synchronized void call(){ System.out.println(Thread.currentThread().getName()+"=> call"); } }
Lock reentrant lock
public class Demo02 { public static void main(String[] args) { Phone2 phone = new Phone2(); new Thread(()->{ phone.sms(); },"A").start(); } class Phone2{ Lock lock=new ReentrantLock(); public void sms(){ lock.lock(); //Details: These are two locks, sms and call //lock locks must be paired and must be released several times after being added, otherwise they will be locked inside try { System.out.println(Thread.currentThread().getName()+"=> sms"); call();//There is also a lock here } catch (Exception e) { e.printStackTrace(); }finally { lock.unlock(); } } public void call(){ lock.lock(); try { System.out.println(Thread.currentThread().getName() + "=> call"); }catch (Exception e){ e.printStackTrace(); } finally { lock.unlock(); } } }
3. Spin lock
Spin lock: when a thread is acquiring a lock, if the lock has been acquired by other threads, the thread will wait in a loop, and then constantly judge whether the lock can be successfully acquired, and will not exit the loop until the lock is acquired. The thread trying to acquire the lock will not be blocked immediately. Try to acquire the lock in a circular way! Reduce context switching! Disadvantages: CPU consumption
Custom spin lock
public class SpinlockDemo { AtomicReference<Thread> atomicReference = new AtomicReference<>(); //Lock public void myLock() { Thread thread = Thread.currentThread(); System.out.println(Thread.currentThread().getName() + "-->myLock"); while (!atomicReference.compareAndSet(null, thread)) { } } //Unlock public void myUnLock() { Thread thread = Thread.currentThread(); System.out.println(Thread.currentThread().getName() + "-->myUnlock"); atomicReference.compareAndSet(thread,null); } }
Test spin lock
public class TestSpinLock { public static void main(String[] args) throws InterruptedException { SpinLockDemo lockDemo = new SpinLockDemo(); new Thread(()->{ lockDemo.myLock(); try{ TimeUnit.SECONDS.sleep(5); }catch (Exception e){ e.printStackTrace(); }finally { lockDemo.myUnlock(); } },"T1").start(); TimeUnit.SECONDS.sleep(1); new Thread(()->{ lockDemo.myLock(); try{ TimeUnit.SECONDS.sleep(1); }catch (Exception e){ e.printStackTrace(); }finally { lockDemo.myUnlock(); } },"T2").start(); } }
Thread T1 gets the lock first, and T2 waits for spin. T2 does not end spin until T1 releases the lock and obtains the lock.
4. Deadlock
Deadlock problem: multiple threads hold each other's required resources, and then form a deadlock. After a deadlock occurs, there will be no exception or prompt, but all threads are blocked and cannot continue.
Four necessary conditions for deadlock generation
- Mutually exclusive condition: a resource can only be used by one process at a time
- Request and hold condition: when a process is blocked by requesting resources, it will hold the obtained resources
- Conditions of non deprivation: the resources obtained by the process cannot be forcibly deprived before they are used up
- Circular waiting condition: a circular waiting resource relationship is formed between several processes
Deadlock Case
import java.util.concurrent.TimeUnit; public class DeadLockDemo { public static void main(String[] args) { String lockA = "lockA"; String lockB = "lockB"; new Thread(new MyThread(lockA,lockB),"T1").start(); new Thread(new MyThread(lockB,lockA),"T2").start(); } } 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(Thread.currentThread().getName()+ " lock:"+lockA + " => get" + lockB); try { TimeUnit.SECONDS.sleep(2); } catch (InterruptedException e) { e.printStackTrace(); } synchronized (lockB){ System.out.println(Thread.currentThread().getName()+ " lock:"+lockB + " => get" + lockA); } } } }
Operation results:
Thread T1 has occupied lock A and is waiting for lock B. thread T2 has occupied lock B and is waiting for lock A. the two threads are deadlocked with each other, resulting in program blocking.
How to troubleshoot deadlock
Use for troubleshooting,
jps(Java Virtual Machine Process Status Tool) is a small tool provided by jdk to view the current java process.
Take the deadlock above as an example for troubleshooting:
1. Use jps -l to locate the process number
2. Use jstack + process number to view stack information to analyze threads