- 各种锁的理解
公平锁
非常公平,不能插队,线程必须按照先来回到的规则调用
不公平锁
非常不公平,可以插队,线程可以允许后来的线程插到先来的前面
举个例子,如果线程A一秒就可以执行完,但是线程B需要一小时才能执行完,那么如果线程A排在了线 程B后面,是不合理的,所以非公平锁允许插队,保证了程序的效率
Java默认就是非公平锁
ReentrantLock就是如此,默认创建就是不公平的
Lock lock=new ReentrantLock(); public ReentrantLock() { sync = new NonfairSync(); }
改为公平锁,其实就是修改它的重载
Lock lock=new ReentrantLock(true); public ReentrantLock(boolean fair) { sync = fair ? new FairSync() : new NonfairSync(); }
可重入锁(递归锁)
所有的锁都是可重入锁,它也叫递归锁
拿到了最外面的锁之后,就可以拿到里面全部的锁,它是自动获得的,比如拿到了家大门的钥匙,就可以拿到各个卧室的锁
synchronized版
package com.wyh.lock; /** * @program: JUC * @description: 重入锁(递归锁) 拿到了最外面的锁 也就自动拿到了里面的锁 * @author: 魏一鹤 * @createDate: 2022-03-13 21:50 **/ // 重入锁(递归锁) 拿到了最外面的锁 也就自动拿到了里面的锁 public class Demo1 { public static void main(String[] args){ //synchronized版重入锁 Phone phone=new Phone(); new Thread(()->{ phone.sendMessage(); },"A").start(); new Thread(()->{ phone.sendMessage(); },"B").start(); } } class Phone{ public synchronized void sendMessage(){ System.out.println(Thread.currentThread().getName()+"发短信"); call();//这里也有一把锁 } public synchronized void call(){ System.out.println(Thread.currentThread().getName()+"打电话"); } }
A发短信
A打电话
B发短信
B打电话
lock版
由于lock是手动版的,需要手动进行加锁解锁,不能忘记解锁,而且配的锁要成对,不然容易造成死锁问题
package com.wyh.lock; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; /** * @program: JUC * @description: 重入锁(递归锁) 拿到了最外面的锁 也就自动拿到了里面的锁 * @author: 魏一鹤 * @createDate: 2022-03-13 21:50 **/ // 重入锁(递归锁) 拿到了最外面的锁 也就自动拿到了里面的锁 public class Demo2 { public static void main(String[] args){ //lock版重入锁 Phone2 phone=new Phone2(); new Thread(()->{ phone.sendMessage(); },"A").start(); new Thread(()->{ phone.sendMessage(); },"B").start(); } } class Phone2{ //创建锁 Lock lock=new ReentrantLock(); public void sendMessage(){ //加锁业务代码放在try中 解锁放在finally中 //细节:锁必须配对 必须解锁 不然就会死锁 try { lock.lock(); lock.lock(); System.out.println(Thread.currentThread().getName()+"发短信"); call();//这里也有一把锁 } catch (Exception e) { e.printStackTrace(); } finally { lock.unlock(); lock.unlock(); } } public void call(){ //加锁业务代码放在try中 解锁放在finally中 try { lock.lock(); System.out.println(Thread.currentThread().getName()+"打电话"); } catch (Exception e) { e.printStackTrace(); } finally { lock.unlock(); } } }
A发短信
A打电话
B发短信
B打电话
自旋锁(spinLock)
自旋锁就是不断的循环遍历迭代,会不断的进行尝试,直到成功为止(达到成功的效果) while 和do while
利用CAS操作模拟写一个自旋锁
package com.wyh.lock; import java.util.concurrent.atomic.AtomicReference; /** * @program: JUC * @description: 自旋锁测试 * @author: 魏一鹤 * @createDate: 2022-03-13 22:13 **/ //自旋锁 不断遍历迭代 直到成功位置 public class SpinLockDemo { //底层使用CAS完成 AtomicReference<Thread> atomicReference=new AtomicReference<>(); //加锁 public void myLock(){ //获取线程 Thread thread = Thread.currentThread(); //获取线程名 System.out.println(thread.getName()+"-------------> myLock"); //自旋锁 while (!atomicReference.compareAndSet(null,thread)){ } } //解锁 public void myUnLock(){ //获取线程 Thread thread = Thread.currentThread(); //获取线程名 System.out.println(thread.getName()+"-------------> myUnLock"); //自旋锁 atomicReference.compareAndSet(thread,null); } }
测试
package com.wyh.lock; import java.util.concurrent.TimeUnit; /** * @program: JUC * @description: 测试自己写的自旋锁 * @author: 魏一鹤 * @createDate: 2022-03-13 22:22 **/ public class TestSpinLock { public static void main(String[] args) throws InterruptedException { //创建我们自己写的自旋锁 底层使用的是CAS SpinLockDemo lock = new SpinLockDemo(); new Thread(()->{ try { //加锁 lock.myLock(); //休眠 TimeUnit.SECONDS.sleep(2); } catch (Exception e) { e.printStackTrace(); } finally { //解锁 lock.myUnLock(); } },"A").start(); //休眠 保证线程A先加锁 TimeUnit.SECONDS.sleep(1); new Thread(()->{ try { //加锁 lock.myLock(); //休眠 TimeUnit.SECONDS.sleep(2); } catch (Exception e) { e.printStackTrace(); } finally { //解锁 lock.myUnLock(); } },"B").start(); } }
A-------------> myLock
B-------------> myLock
A-------------> myUnLock
B-------------> myUnLock
死锁排查
死锁怎么造成的?
线程A持有自己的锁,线程B持有自己的锁,他们两个试图争抢对方的资源,一直僵持,就造成了死锁
package com.wyh.lock; import lombok.SneakyThrows; import java.util.concurrent.TimeUnit; /** * @program: JUC * @description: 死锁测试 * @author: 魏一鹤 * @createDate: 2022-03-13 22:36 **/ //死锁测试 排查死锁 其实就是两个线程互相抢别人的资源 public class DeadLockDemo { public static void main(String[] args){ String lockA = "lockA"; String lockB = "lockB"; //线程A想拿线程B的锁 线程B想拿线程A的锁 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; } @SneakyThrows @Override public void run() { synchronized (lockA) { System.out.println(Thread.currentThread().getName() + "lock:"+lockA+"get==>"+lockB); //休眠2s TimeUnit.SECONDS.sleep(2); synchronized (lockB) { System.out.println(Thread.currentThread().getName() + "lock:"+lockB+"get==>"+lockA); } } } }
T1lock:lockAget==>lockB
T2lock:lockBget==>lockA
程序一直由于死锁卡着,如何解决这个问题呢?
命令排查死锁
1 使用java命令 jps -l定位进程号
2 使用jstack 进程号 查看进程信息,找到死锁原因
开发中一般都是查看日志或者堆栈信息