java 可重入锁

Posted 偶像java练习生

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了java 可重入锁相关的知识,希望对你有一定的参考价值。

1.公平锁,非公平锁

公平锁:非常公平,不能插队,必须先来后到!
非公平锁:非常不公平,可以插队,例子:一个线程3s 执行完,一个线程3h 执行完,先让3s 的执行,需要插队!

Lock lock = new ReentrantLock();
	public ReentrantLock() {
	sync = new NonfairSync();
}

ReentranLock 默认是非公平锁
但是可以通过他的构造方法改变为公平锁: 如下

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

synchronized 锁也是非公平锁,所有的锁默认都是非公平锁,保证我们一个效率问题。

2. 可重入锁

所有的锁都是可重入锁,拿到外面的锁之后,就可以拿到里面的锁,自动获得
在这里插入图片描述
代码如下:

package com.lock;

//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");
    }

}

输出结果:
Asms
Acall
Bsms
Bcall
A 拿到锁后先执行完毕,再B 执行

下面是LOCK 版的:

package com.lock;

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

public class Demo02 {


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

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

    }

class Phone2{


        Lock lock = new ReentrantLock();

        public synchronized void sms(){
            lock.lock();//细节问题:lock.lock 是拿的两把锁,lock.lock;,lock.unlock
            lock.lock()
            //lock 锁必须配对,否则就会死在里面
            try {
                System.out.println(Thread.currentThread().getName()+"sms");
                call();//这里面也有锁
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
                lock.unlock();
            }
        }

        public synchronized void call(){
            lock.lock();
            try {
                System.out.println(Thread.currentThread().getName()+"call");
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }
        }

    }


}

输出结果:
Asms
Acall
Bsms
Bcall

3.自旋锁

spinlock
自旋锁:不断的去尝试,直到成功为止!

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

我们来自定义下自旋锁:

package com.lock;

import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;

public class TestSpinLock {


 public static void main(String[] args) throws InterruptedException {
//        ReentrantLock reentrantLock = new ReentrantLock();
//        reentrantLock.lock();
//        reentrantLock.unlock();

     //底层使用的自旋锁
     SpinLockDemo lock = new SpinLockDemo();
     new Thread(()->{
         try {
             lock.myLock();
             TimeUnit.SECONDS.sleep(3);
         } catch (Exception e) {
             e.printStackTrace();
         } finally {
             lock.myUnLock();
         }
     },"T1").start();

     TimeUnit.SECONDS.sleep(1);

     new Thread(()->{
         try {
             lock.myLock();
             TimeUnit.SECONDS.sleep(1);
         } catch (Exception e) {
             e.printStackTrace();
         } finally {
             lock.myUnLock();
         }
     },"T2").start();


     lock.myLock();
     lock.myUnLock();



 }


}

输出结果:
T1===> mylock
main===> mylock
T2===> mylock
T1==>myUnlock
T2==>myUnlock
main==>myUnlock

T1 解锁后T2 才能解锁,T1 在自旋,T1 解锁后,T2 解锁

4.死锁

在这里插入图片描述
死锁测试,怎么排除死锁:

package com.lock;

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

    }
}

输出结果:
T2lock:lockB=>getlockA
T1lock:lockA=>getlockB
程序卡住了

解决程序卡住了的问题
1.使用jps 定位进程号
linux ls-l

而jps 则用jps -l 命令如下:

在这里插入图片描述
F:\\workSpace\\AWT>jps -l
2128 sun.tools.jps.Jps
2344 com.lock.DeadLockDemo
8024
15100 org.jetbrains.jps.cmdline.Launcher

可以查看到当前死锁的进程为 2344

  1. 使用 jstack 进程号 进程号找到死锁问题
    使用命令 jstack -2344 显示如下,翻到最底下:
    在这里插入图片描述
    面试或者工作中! 排查问题:
    1.进程卡了看上线上有没有异常?
    异常:
  2. 使用日志查看
  3. 使用堆栈,信息
    如何解决死锁?
    解决死锁产生的条件就可以了!

以上是关于java 可重入锁的主要内容,如果未能解决你的问题,请参考以下文章

java中可重入锁和自旋锁

可重入锁

java 多线程-可重入锁

ReentrantLock可重入锁在我们的代码中。

什么是重入锁和AQS

Java并发程序设计(12)并发锁之可重入锁ReentrantLock