Small knowledge, big challenge! This article is participating in the creation activity of “Essential Tips for Programmers”

This article has participated in the “Digitalstar Project” and won a creative gift package to challenge the creative incentive money.

1, fair lock, not fair lock

A fair lock means that when the lock is available, the thread that has waited the longest on the lock gains access to the lock and must come first.

//ReentrantLock(true) Sets to fair lock
public ReentrantLock(boolean fair) {
    sync = fair ? new FairSync() : new NonfairSync();
}

Copy the code

Non-fair lock is randomly assigned this right to use, can jump the queue. Sometimes, some threads take a long time to execute and other threads have to wait a long time if they need to lock, so we generally use unfair locks to provide efficiency

//ReentrantLock() defaults to unfair lock
public ReentrantLock(a) {
    sync = new NonfairSync();
}
Copy the code

2. Reentrant lock

A reentrant lock, also called a recursive lock, means that when the same thread obtains the lock from the outer method, the inner method automatically acquires the lock. The JDK basically uses reentrant locks to avoid deadlocks.

Synchronized can re-enter the 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(a){
        System.out.println(Thread.currentThread().getName()+"=> sms");
        call();// Inner method
    }
    // Lock the inner method
    public synchronized void call(a){
        System.out.println(Thread.currentThread().getName()+"=> call"); }}Copy the code

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(a){
        lock.lock(); // Details: This is the two locks, respectively SMS and call
        // Lock the lock must be paired and released several times, otherwise it will be locked inside
        try {
            System.out.println(Thread.currentThread().getName()+"=> sms");
            call();// There is also a lock
        } catch (Exception e) {
            e.printStackTrace();
        }finally{ lock.unlock(); }}public void call(a){
        lock.lock();
        try {
            System.out.println(Thread.currentThread().getName() + "=> call");
        }catch (Exception e){
            e.printStackTrace();
        }
        finally{ lock.unlock(); }}}Copy the code

3. Spin locks

Spinlock: When a thread is acquiring a lock, if the lock has already been acquired by another thread, the thread will wait in a loop, and then continuously determine whether the lock can be acquired successfully, until the lock is acquired and will exit the loop. The thread that tries to acquire the lock does not block immediately. It tries to acquire the lock in a loop! Reduce context switching! The disadvantage is CPU consumption

Custom spin lock

public class SpinlockDemo {
    AtomicReference<Thread> atomicReference = new AtomicReference<>();

    / / lock
    public void myLock(a) {
        Thread thread = Thread.currentThread();
        System.out.println(Thread.currentThread().getName() + "-->myLock");
        while(! atomicReference.compareAndSet(null, thread)) {

        }
    }
    / / unlock
    public void myUnLock(a) {
        Thread thread = Thread.currentThread();
        System.out.println(Thread.currentThread().getName() + "-->myUnlock");
        atomicReference.compareAndSet(thread,null); }}Copy the code

Test spin locks

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(); }}Copy the code

Thread T1 picks up the lock first, and thread T2 waits in spin until T1 releases the lock, and then thread T2 finishes spinning and picks up the lock.

4, a deadlock

Deadlock problem: multiple threads holding resources each other need, and then deadlock. After a deadlock occurs, there are no exceptions, no hints, but all threads are blocked and cannot continue.

Four necessary conditions for deadlock

  • Mutually exclusive: a resource can only be used by one process at a time
  • Request and hold conditions: when a process is blocked by requesting resources, it holds on to acquired resources
  • Non-dispossession condition: a process cannot forcibly take away a resource it has acquired until it is 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(a) {
        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); }}}}Copy the code

Running results:

Thread T1 has held lock A and is waiting for lock B. Thread T2 has held lock B and is waiting for lock A. The two threads are locked, causing the program to block.

How to check for deadlocks

Use it for screening,

The Java Virtual Machine Process Status Tool (JPS) is a small Tool provided by the JDK to view the current Java Process.

Take the deadlock above as an example.

1. Use JPS -L to locate the process NUMBER

2. Use jStack + process number to view stack information to analyze thread status