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