2022.8.21 各种锁理解

21、各种锁理解

1、公平锁和非公平锁:

公平锁:非常公平,不能够插队,必须先来后到!FIFO

非公平锁:非常不公平,可以插队(默认都是非公平)

2、可重入锁

递归锁

可重入锁synchronized 版本

 1  package com.xing.lock;
 2  3  //Synchorized
 4  public class Demo01 {
 5      public static void main(String[] args) {
 6          Phone phone = new Phone();
 7          new Thread(()->{
 8              phone.sms();
 9          },"a").start();
10          new Thread(()->{
11              phone.sms();
12          },"b").start();
13 14      }
15  }
16  class Phone{
17      public synchronized void sms(){
18          System.out.println(Thread.currentThread().getName() + "sms");
19          call(); //这里也有锁(sms锁 里面的call锁)
20      }
21      public synchronized void call(){
22          System.out.println(Thread.currentThread().getName() + "call");
23      }
24  }

可重入锁Lock版本(递归锁 )

 1  package com.xing.lock;
 2  3  import java.util.concurrent.locks.Lock;
 4  import java.util.concurrent.locks.ReentrantLock;
 5  6  public class Demo02 {
 7      public static void main(String[] args) {
 8          Phone2 phone = new Phone2();
 9          new Thread(()->{
10              phone.sms();
11          },"a").start();
12          new Thread(()->{
13              phone.sms();
14          },"b").start();
15 16      }
17  }
18  class Phone2{
19      Lock lock = new ReentrantLock();
20      public void sms(){
21          lock.lock();//细节问题 第一把锁
22          //lock锁必须配对否则就会死在里面
23          try {
24              System.out.println(Thread.currentThread().getName() + "sms");
25              call(); //这里也有锁(sms锁 里面的call锁)
26          } catch (Exception e) {
27              e.printStackTrace();
28          }finally {
29              lock.unlock();
30          }
31 32      }
33      public synchronized void call(){
34          lock.lock();//第二把锁
35          try {
36              System.out.println(Thread.currentThread().getName() + "call");
37          } catch (Exception e) {
38              e.printStackTrace();
39          }finally {
40              lock.unlock();
41          }
42 43 44      }
45  }
46

 

总结:两个锁只要获得外面的锁之后,就能够拿到里面的锁,Lock和synchronized 的区别在需要手动释放锁,并且枷锁的次数和释放的次数要一样

3、自旋锁( spinlock )

不断尝试直到成功为止

自己创建一个自己的锁用CAS完成

 1  package com.xing.lock;
 2  3  import java.util.concurrent.atomic.AtomicReference;
 4  5  //自己写的自旋锁
 6  public class SpinlockDemo {
 7      //Thread null
 8      AtomicReference<Thread> atomicReference = new AtomicReference();
 9 10      // 加锁
11      public void myLock() {
12          Thread thread = Thread.currentThread();
13          System.out.println(Thread.currentThread().getName() + "==>mylock");
14          //自旋锁
15          while (!atomicReference.compareAndSet(null, thread)) {
16 17          }
18      }
19      // 解锁
20      public void myUnLock() {
21          Thread thread = Thread.currentThread();
22          System.out.println(Thread.currentThread().getName() + "==>myunlock");
23          atomicReference.compareAndSet(thread, null);
24      }
25  }
26

然后去用线程调用

 1  package com.xing.lock;
 2  3  import java.util.concurrent.TimeUnit;
 4  5  public class TestSpinlock {
 6      public static void main(String[] args) {
 7          /*ReentrantLock reentrantLock = new ReentrantLock();
 8          reentrantLock.lock();
 9          reentrantLock.unlock();*/
10          
11          //底层的是自旋锁CAS实现的
12          SpinlockDemo spinlockDemo = new SpinlockDemo();
13 14          new Thread(()->{
15              spinlockDemo.myLock();
16              try {
17                  TimeUnit.SECONDS.sleep(3);
18              } catch (InterruptedException e) {
19                  e.printStackTrace();
20              }finally {
21                  spinlockDemo.myUnLock();
22              }
23          },"T1").start();
24 25          //T1获得锁,T2一直在自旋。T1解锁后,T2才获得锁结束自旋
26          new Thread(()->{
27              spinlockDemo.myLock();
28              try {
29                  TimeUnit.SECONDS.sleep(1);
30              } catch (InterruptedException e) {
31                  e.printStackTrace();
32              }finally {
33                  spinlockDemo.myUnLock();
34              }
35          },"T2").start();
36 37 38      }
39  }
40

 

总结:B线程这里一定会等待T1完成比较交换这个时候自旋锁里已经被A占着了(自旋锁条件:线程不为空及自转)所以B线程会一直自旋到A线程完成CAS的比较交换完成才不在进行自旋操作再进入到myunlock里完成解锁操作

优点:

自旋锁不会使线程状态发生切换,一直处于用户态,即线程一直都是active的;不会使线程进入阻塞状态,减少了不必要的上下文切换,执行速度快。

缺点:

如果某个线程持有锁的时间过长,就会导致其它等待获取锁的线程进入循环等待,消耗CPU。使用不当会造成CPU使用率极高。

上面Java实现的自旋锁不是公平的,即无法满足等待时间最长的线程优先获取锁。不公平的锁就会存在“线程饥饿”问题。

4、死锁

死锁测试,怎么排除死锁

 1  package com.xing.lock;
 2  3  import single.LazyMan;
 4  5  import java.util.concurrent.TimeUnit;
 6  7  public class DeadlockDemo {
 8      public static void main(String[] args) {
 9          String lockA = "lockA";
10          String lockB = "lockB";
11          // A想拿B
12          new Thread(new MyThread(lockA,lockB),"T1").start();
13          // B想拿A
14          new Thread(new MyThread(lockB,lockA),"T2").start();
15 16 17      }
18  }
19  class MyThread implements Runnable{
20      // 两个资源
21      private String lockA;
22      private String lockB;
23 24      public MyThread(String lockA, String lockB) {
25          this.lockA = lockA;
26          this.lockB = lockB;
27      }
28 29      @Override
30      public void run() {
31          synchronized (lockA){
32              //lockA想拿B
33              System.out.println(Thread.currentThread().getName() + "lock"+ lockA + "=> get" + lockB);
34              try {
35                  TimeUnit.SECONDS.sleep(3);
36              } catch (InterruptedException e) {
37                  e.printStackTrace();
38              }
39              synchronized (lockB){
40                  //B想拿A
41                  System.out.println(Thread.currentThread().getName() + "lock" + lockB + "=> get" + lockA);
42              }
43          }
44      }
45  }
46

 

死锁排查

1、使用jps -l定位进程号

例如:在终端输入jps -l

2、使用jstack 进程号 找到死锁问题

例如:jstack 11444

 

posted @ 2022-08-21 23:39  暴躁C语言  阅读(109)  评论(0编辑  收藏  举报