各种锁的理解

简介: 各种锁的理解
  1. 各种锁的理解

公平锁

 非常公平,不能插队,线程必须按照先来回到的规则调用

不公平锁

非常不公平,可以插队,线程可以允许后来的线程插到先来的前面

举个例子,如果线程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)


image.png

自旋锁就是不断的循环遍历迭代,会不断的进行尝试,直到成功为止(达到成功的效果) 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 进程号 查看进程信息,找到死锁原因

开发中一般都是查看日志或者堆栈信息

目录
打赏
0
0
0
0
3
分享
相关文章
网络技术基础(10)—— VLAN间通信
【3月更文挑战第2天】网络基础笔记(加班了几天,中途耽搁了,预计推迟6天),这篇借鉴了之前师兄的笔记,边听边记笔记实在是太慢了。
插件实现12306网站“按预填信息”自动抢票
年底抢票回家过年,12306的“按预填信息购票”功能却隐藏按钮,导致抢票困难。程序员发现按钮被CSS隐藏后尝试手动修改,但遇到自动恢复和确认窗口的问题。最终决定开发Chrome插件,通过监听页面按钮自动点击,实现一键抢票。该插件结构简单,包含manifest.json、popup.html等文件,能有效节省抢票时间。代码已开源至GitHub,供有需要的人参考使用。
spring data elasticsearch 打印sql(DSL)语句
spring data elasticsearch 打印sql(DSL)语句
669 0
【AI应用落地实战】智能文档处理本地部署——可视化文档解析前端TextIn ParseX实践
2024长沙·中国1024程序员节以“智能应用新生态”为主题,吸引了众多技术大咖。合合信息展示了“智能文档处理百宝箱”的三大工具:可视化文档解析前端TextIn ParseX、向量化acge-embedding模型和文档解析测评工具markdown_tester,助力智能文档处理与知识管理。
MaxKB创建本地知识库
这篇文章详细介绍了如何使用MaxKB创建本地知识库,并通过上传文档来构建个性化的问答系统,使得大模型可以根据上传的知识内容来回答问题。
529 0
MaxKB创建本地知识库
https网站
https网站
857 1
AI助理
登录插画

登录以查看您的控制台资源

管理云资源
状态一览
快捷访问

你好,我是AI助理

可以解答问题、推荐解决方案等