Bootstrap

JUC详解-19-各种锁的理解

JUC 详解 -> 各种锁的理解

1. 公平锁、非公平锁
  • 公平锁:非常公平,不能插队,必须先来后到
  • 非公平锁:非常不公平,可以插队,(默认都是非公平锁)

java的ReenTrantLock就是用队列实现的公平锁和非公平锁。

公平锁中,如果有另一个线程持有锁或者有其他线程在等待队列中等待这个锁,那么新发出请求的线程将被放入到队列中。而非公平锁上,只有当锁被某个线程持有时,新发出请求的线程才会被放入队列中(此时和公平锁是一样的)。所以,它们的差别在于非公平锁会有更多的机会去抢占锁。

public ReentrantLock() {
    sync = new NonfairSync();
}
public ReentrantLock(boolean fair) {
    sync = fair ? new FairSync() : new NonfairSync();
}
2. 可重入锁
  • 可重入锁,也叫递归锁

可重入锁

//Synchronized版
public class Demo01 {
    public static void main(String[] args) {
        Phone phone = new Phone();
        new Thread(()-> {
            phone.sms();
        },"A").start();

        new Thread(()-> {
            phone.sms();
        },"B").start();
    }
}
class Phone{
    public synchronized void sms(){
        System.out.println(Thread.currentThread().getName()+" sms");
        call();
    }

    public synchronized void call(){
        System.out.println(Thread.currentThread().getName()+" call");
    }
}
//Lock版
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

//ReentrantLock
public class Demo02 {
    public static void main(String[] args) {
        Phone2 phone2 = new Phone2();
        new Thread(()->{
            phone2.sms();
        },"A").start();

        new Thread(()->{
            phone2.sms();
        },"B").start();
    }
}

class Phone2{
    Lock lock = new ReentrantLock();
    public void sms(){
        lock.lock(); //细节问题!两把锁,进到call(),也会拿到一把锁!
        //lock.lock(); 死锁
        //lock锁必须配对
        try{
            System.out.println(Thread.currentThread().getName() + " sms");
            call();
        }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. 自旋锁
  • spinlock
自旋锁 ![自旋锁](https://img-blog.csdnimg.cn/20201124000502441.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2JhaWR1XzM4MTI2MzA2,size_16,color_FFFFFF,t_70#pic_center)
  • 自定义自旋锁
import java.util.concurrent.atomic.AtomicReference;

//自旋锁
public class SpinLockDemo {
    AtomicReference<Thread> atomicReference = new AtomicReference<>();
    //加锁
    public void myLock(){
        Thread thread = Thread.currentThread();
        System.out.println(Thread.currentThread().getName()+"==> myLock");
        //自旋锁
        while(!atomicReference.compareAndSet(null,thread)){

        }
    }

    //解锁
    public void myUnlock(){
        Thread thread = Thread.currentThread();
        System.out.println(Thread.currentThread().getName()+"==> myUnlock");
        atomicReference.compareAndSet(thread,null);
    }
}
  • 测试
import java.util.concurrent.TimeUnit;

public class TestSpinLock {
    public static void main(String[] args) throws InterruptedException {
        //底层使用的是自旋锁CAS
        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();
    }
}
4. 死锁

死锁是什么?

  • 四要素:互斥、占有等待、循环等待、不可抢占

死锁测试

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);

            }
        }
    }
}

如何排除死锁,解决问题

  • 1 使用 **jps -l **定位进程号

jps

  • 2 使用jstack 进程号查看进程信息

jstack

;