各种锁的理解

简介: 各种锁的理解
  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 进程号 查看进程信息,找到死锁原因

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

目录
相关文章
|
安全 Java 数据安全/隐私保护
|
网络协议 网络安全 网络虚拟化
网络技术基础(10)—— VLAN间通信
【3月更文挑战第2天】网络基础笔记(加班了几天,中途耽搁了,预计推迟6天),这篇借鉴了之前师兄的笔记,边听边记笔记实在是太慢了。
|
开发工具 Android开发 数据安全/隐私保护
Cocos Creator Android 平台 Facebook 原生登录(一)
Cocos Creator Android 平台 Facebook 原生登录
774 0
|
9月前
|
Web App开发 JavaScript 前端开发
插件实现12306网站“按预填信息”自动抢票
年底抢票回家过年,12306的“按预填信息购票”功能却隐藏按钮,导致抢票困难。程序员发现按钮被CSS隐藏后尝试手动修改,但遇到自动恢复和确认窗口的问题。最终决定开发Chrome插件,通过监听页面按钮自动点击,实现一键抢票。该插件结构简单,包含manifest.json、popup.html等文件,能有效节省抢票时间。代码已开源至GitHub,供有需要的人参考使用。
|
SQL Java 关系型数据库
spring data elasticsearch 打印sql(DSL)语句
spring data elasticsearch 打印sql(DSL)语句
790 0
|
12月前
|
人工智能 资源调度 数据可视化
【AI应用落地实战】智能文档处理本地部署——可视化文档解析前端TextIn ParseX实践
2024长沙·中国1024程序员节以“智能应用新生态”为主题,吸引了众多技术大咖。合合信息展示了“智能文档处理百宝箱”的三大工具:可视化文档解析前端TextIn ParseX、向量化acge-embedding模型和文档解析测评工具markdown_tester,助力智能文档处理与知识管理。
|
12月前
|
搜索推荐 数据库
MaxKB创建本地知识库
这篇文章详细介绍了如何使用MaxKB创建本地知识库,并通过上传文档来构建个性化的问答系统,使得大模型可以根据上传的知识内容来回答问题。
835 0
MaxKB创建本地知识库
|
Java 网络安全 开发工具
UNITY与安卓⭐一、Android Studio初始设置
UNITY与安卓⭐一、Android Studio初始设置
|
前端开发 中间件 API
CORS的具体实现
CORS的具体实现
197 2
|
SQL Java Shell
实时计算 Flink版产品使用问题之 Application模式下,如何设置环境变量
实时计算Flink版作为一种强大的流处理和批处理统一的计算框架,广泛应用于各种需要实时数据处理和分析的场景。实时计算Flink版通常结合SQL接口、DataStream API、以及与上下游数据源和存储系统的丰富连接器,提供了一套全面的解决方案,以应对各种实时计算需求。其低延迟、高吞吐、容错性强的特点,使其成为众多企业和组织实时数据处理首选的技术平台。以下是实时计算Flink版的一些典型使用合集。