天天看点

JUC(12)各种锁

文章目录

  • ​​一、公平锁与非公平锁​​
  • ​​二、可重入锁​​
  • ​​三、自旋锁​​
  • ​​四、死锁​​

一、公平锁与非公平锁

  • 公平锁:非常公平;不能插队的,必须先来后到;
  • 非公平锁:非常不公平,允许插队的,可以改变顺序。(ReentrantLock 与 Synchronized 默认都是非公平锁)
/**
 * Creates an instance of {@code ReentrantLock}.
 * This is equivalent to using {@code ReentrantLock(false)}.
 */
public ReentrantLock() {
    sync = new NonfairSync();
}

/**
 * Creates an instance of {@code ReentrantLock} with the
 * given fairness policy.
 *
 * @param fair {@code true} if this lock should use a fair ordering policy
 */
public ReentrantLock(boolean fair) {
    sync = fair ? new FairSync() : new NonfairSync();
}      

二、可重入锁

  • 可重入锁(递归锁)
  • 什么是 “可重入”,可重入就是说某个线程已经获得某个锁,可以再次获取相同的锁而不会出现死锁。
  • synchronized和ReentrantLock都是可重入的
  • 使用ReentrantLock的注意点 : synchronized是自己释放锁的,ReentrantLock 和 synchronized 不一样,需要手动释放锁,所以使用 ReentrantLock的时候一定要手动释放锁,并且加锁次数和释放次数要一样(否则就导致死锁)
  • synchronized
package com.wlw.allLock;

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

        new Thread(()->{
            phone.send();
        },"B").start();
    }
}
class Phone{

    public synchronized void send(){
        System.out.println(Thread.currentThread().getName()+"===》 send message");
        call();
    }

    public synchronized void call(){
        System.out.println(Thread.currentThread().getName()+"===》 call");
    }
}

/*
A===》 send message
A===》 call
B===》 send message
B===》 call
*/      
  • ReentrantLock
package com.wlw.allLock;

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

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

        new Thread(()->{
            phone.send();
        },"B").start();
    }
}
class Phone2{

    Lock lock = new ReentrantLock();

    public void send(){
        lock.lock();
        try {
            System.out.println(Thread.currentThread().getName()+"===》 send message");
            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();
        }

    }
}
/*
A===》 send message
A===》 call
B===》 send message
B===》 call
*/      

三、自旋锁

  • spinlock自旋锁:不断的循环,不断的迭代,不断的尝试,知道成功为止
  • 之前在原子类 (AtomicInteger.getAndIncrement->unsafe.getAndAddInt)中见过自旋锁
public final int getAndAddInt(Object var1, long var2, int var4) {
        int var5;
        do {
            var5 = this.getIntVolatile(var1, var2);
        } while(!this.compareAndSwapInt(var1, var2, var5, var5 + var4));

        return var5;
}      
  • 自己利用CAS写一个自旋锁
package com.wlw.allLock;

import java.util.concurrent.atomic.AtomicReference;
//自旋锁
public class SpinLockDemo {
    AtomicReference atomicReference = new AtomicReference();

    //加锁
    public void mylock(){
        Thread thread = Thread.currentThread();
        System.out.println(Thread.currentThread().getName()+"===> mylock");

        //自旋锁 第一个线程执行mylock方法,不会进入循环,第二线程才会进入
        while (!atomicReference.compareAndSet(null,thread)){

        }
    }

    //解锁
    public void myunlock(){
        Thread thread = Thread.currentThread();
        System.out.println(Thread.currentThread().getName()+"===> myunlock");
        //设置为null,解锁
        atomicReference.compareAndSet(thread,null);
    }
}      
package com.wlw.allLock;

import java.util.concurrent.TimeUnit;

//测试自旋锁
public class TestSpinLock {
    public static void main(String[] args) throws InterruptedException {
        SpinLockDemo spinLockDemo = new SpinLockDemo();

        new Thread(()->{
            spinLockDemo.mylock();
            try {
                TimeUnit.SECONDS.sleep(5);
            }catch (Exception e){
                e.printStackTrace();
            }
            finally {
                spinLockDemo.myunlock();
            }
        },"T1").start();

        TimeUnit.SECONDS.sleep(1);

        new Thread(()->{
            spinLockDemo.mylock();
            try {
                TimeUnit.SECONDS.sleep(5);
            }catch (Exception e){
                e.printStackTrace();
            }
            finally {
                spinLockDemo.myunlock();
            }
        },"T2").start();
    }
}
/*
T1===> mylock
T2===> mylock
T1===> myunlock
T2===> myunlock
T2进程必须等待T1进程Unlock后,才能Unlock,在这之前进行自旋等待。。。。
*/      

四、死锁

  • 什么是死锁?
  • 如果一组进程中的每一个进程都在等待仅有改组进程中的其他进程才能引发的事件,那么改组进程是死锁的
  • 简单点来说,第一个线程拥有A对象锁标记,并等待B对象锁标记,同时第二个线程拥有B对象锁标记,并等待A对象锁标记时,产生死锁。(或者来说,两个进程都在等待对方释放持有的资源,而自己却不释放资源)
  • 产生死锁的必要条件:
  • 互斥条件:资源是互斥使用的
  • 请求和保持条件:该线程保持自己持有的资源,去请求其他进程已经占有的资源
  • 不可抢占条件:进程已获得的资源在未使用完之前不能被抢占
  • 循环等待条件:发生死锁时,必然存在着一个 进程——资源 的循环链
package com.wlw.allLock;

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()+"持有"+lockA+",想要"+lockB);

            synchronized (lockB){
                System.out.println(Thread.currentThread().getName()+"持有"+lockB+",想要"+lockA);
            }
        }
    }
}
/*
t1持有lockA,想要lockB
t2持有lockB,想要lockA
程序就这样一直持续下去....................
*/      
  • 怎么解决死锁呢?
  • 当我们发现程序出现死锁时,可以使用jdk中bin目录下有一个jps工具来定位死锁
  • 1、使用jps定位进程号,命令 jps -l
  • 2、使用​

    ​jstack进程号​

    ​ 找到死锁信息
  • 排查问题!:1、日志 , 2、堆栈信息