Java 中线程相关的各种锁

news2024/12/28 5:19:45

一、Java对象与锁

1、对象结构

2、对象头的 Mark Word

二、锁介绍

1、概念和种类

1、乐观锁

不加锁,在使用数据时判断数据是不是最新。常用CAS算法实现

2、自旋锁 与 适应性自旋锁

两者并不是锁,而是锁提供的处理方式。

自旋锁(JDK1.4):
阻塞或唤醒一个Java线程需要操作系统切换CPU状态来完成,这种状态转换需要耗费处理器时间。
如果代码的逻辑较简单,那让抢不到锁的线程不放弃CPU的执行时间,选择“稍等一下”,比线程阻塞和切换更加节约时间。
适应性自旋锁(JDK6):
自旋的时间(次数)不再固定,由前一次在同一个锁上的自旋时间及锁的拥有者的状态来决定。
如果上次自旋成功获得锁,那虚拟机将允许自旋等待持续相对更长的时间。
如果对于某个锁,自旋很少成功获,那以后尝试获取这个锁时将可能直接阻塞线程。

3、无锁、偏向锁、轻量级锁、重量级锁

针对synchronized锁的

1.无锁、偏向锁

当无线程竞争时,为了减少资源开销,对资源不加锁,或者偏向某频繁访问的线程,两者的标识位都是'01'。
无锁没有对资源进行锁定,当竞争升级时锁会升级为偏向锁。
偏向锁
一个锁被某个线程访问频繁,那么该线程会自动获取锁,通过CAS替换MarkWord中的偏向锁标记和锁标记,并设置线程 ID。

2.轻量级锁

如果竞争再激烈些,偏向锁会升级为轻量级锁,其他线程会通过自旋的形式(while(...))尝试获取锁,不阻塞。
    轻量级锁加锁:
        1.在线程进入同步块时,如果同步对象锁状态为无锁状态(偏向锁为0,锁标志位为01),虚拟机先将在当前线程的栈帧中创建一个锁记录(Lock Record)空间,用于存储锁对象目前的 Mark Word 拷贝,
        2.复制对象头中的 Mark Word 到锁记录(Lock Record)中.
        3.复制完后,虚拟机将使用 CAS 操作尝试将对象的 Mark Word 更新为指向LockRecord的指针,并将 Lock Record 里的 owner 指针指向对象头的 mark word.
        4.如果1-3成功了,那么这个线程就拥有了该对象的锁,并将对象MarkWord的锁标记设置为 00,即表示此对象处于轻量级锁定状态.
        5.如果1-3失败了,虚拟机首先会检查对象的Mark Word 是否指向当前线程的栈帧,不是则有多个线程竞争锁。进行自旋,等待占用锁的线程释放锁,通过 CAS 后即可立即获取锁,否则轻量级锁就要升级为重量。
    轻量级锁释放:
        轻量级锁释放锁(解锁)时,会使用 CAS 将之前复制在栈桢中的Displaced Mard Word替换回 Mark Word 中。

3.重量级锁

升级为重量级锁时,锁标志的状态值变为”10“,锁对象与一个monitor关联 mark word 指向 拥有锁线程的 monitor 的指针。
提供了排它互斥访问机制,保证在每个时间点上最多只有一个线程会执行同步方法。等待锁的线程都会进入阻塞状态。
阻塞或者唤醒一个线程时,要操作系统来调度,用户态转换到内核态比较耗时。
    重量级锁加锁
      重量级锁是通过对象内部的监视器(monitor)来实现,也就是通过ObjectMonitor 实现,ObjectMonitor.hpp 有几个重要属性:
        _Owner:保存当前持有锁的线程;
        _EntryList:等待获取锁的线程;
        _WaitSet:调用 Object 的 wait()方法等待时,此时将该等待的线程保存到_WaitSet中;
        _cxq:存储没有获取到锁的线程;
        _recursions:记录重入次数;
      当多个线程同时访问某段同步代码时:
        1、首先会进入_EntryList 集合;
        2、当线程获取到对象的 monitor 之后,就会进入_Owner 区域,并把ObjectMonitor对象的_Owner 指向为当前线程,并将_count + 1;
        3、如果调用了释放锁(比如 wait()方法)操作,就会释放当前持有的monitor,即_owner= null,_count - 1,同时这个线程会进入到_WaitSet 列表中等待被唤醒;
        4、如果当前线程执行完毕,则释放 monitor 锁,复位_count 的值(_count-1),不过此时不会进入_WaitSet 列表;
    重量级锁释放
        锁释放是同步代码块执行结束后触发,ObjectMonitor::exit;
        1、ObjectMonitor 中持有的锁的_owner 对象置为 null;
        2、从_cxq 队列中唤醒一个被挂起的线程: 根据 QMode 模式判断是从_cxq 还是 EntryList 中获取头节点的线程进行唤醒,通过ObjectMonitor::ExitEpilog 方法唤醒该节点封装的线程,唤醒操作最终由unpark完成:_cxq(竞争列表)EntryList(锁候选者列表)
        3、被唤醒的线程重新竞争重量级锁,被唤醒的线程不一定能抢到锁,未抢到锁的线程将继续挂起,synchronized 是非公平锁;

4、公平锁 与 非公平锁

1.公平锁

多个线程按照申请锁的顺序入队列中排队来获取锁。
每个线程都会获得执行;
除了第一个线程外都会阻塞,CPU唤醒阻塞线程的开销比非公平锁大,效率低,

2.非公平锁

多个线程加锁时直接尝试获取锁,获取不到去队尾等待。
可以减少唤起线程的开销,整体的吞吐效率高。
有的线程线程可能等很久才会获得锁,或者根本获取不到。

5、可重入锁(递归锁)

同一个线程对同一把锁在不释放的前提下,反复加锁不会导致线程的卡死,要保证 unlock() 的次数和 lock()一样的多。
当 state== 0 时锁是空闲的,大于零表示锁已经被占用,它的数值表示当前线程重复占用这个锁的次数。
线程在获取了锁之后,再次去获取了同一个锁,仅是把状态值累加。线程释放了一次锁,仅把状态值减了,线程 A 把此锁全部释放了,状态值减到 0 了,其他线程才有机会获取锁。
ReentrantLock 和 synchronized都是可重入锁,重入锁可一定程度避免死锁。

6、独享锁 与 共享锁

独享锁与共享锁是通过AQS来实现的

1.独享锁

也叫排他锁,该锁一次只能被一个线程所持有。
如果线程T对数据A加上排它锁后,其他线程不能再对A加任何类型的锁。
获得独享锁的线程可读、可改数据。

2.共享锁

可被多个线程所持有。
如果线程T对数据A加上共享锁后,则其他线程只能对A再加共享锁,不能加排它锁。
获得共享锁的线程只能读数据,不能修改数据。

2、CAS机制 与 AQS机制

1、CAS机制

CAS 全称是 Compare And Swap(比较再交换)
CAS算法有三个操作数,通过内存中的值(V)、预期原始值(A)、修改后的新值。
比较 A 与 V 是否相等
如果比较相等,将 B 写入 V
CAS只能保证变量的原子性,不能保证变量的内存可见性。CAS获取共享变量的值时,需要和volatile配合使用,来保证共享变量的可见性,否则会出现 ABA问题。

2、AQS机制

抽象的队列同步器。
AQS用CLH队列锁实现的,即将暂时获取不到锁的线程加入到队列中。
用一个int成员变量来表示同步状态,通过内置的FIFO队列来完成获取资源线程的排队工作。AQS使用CAS对该同步状态进行原子操作实现对其值的修改。
CLH(Craig,Landin,and Hagersten)

虚拟的双向队列(即不存在队列实例,仅存在结点之间的关联关系)。AQS是将每条请求共享资源的线程封装成一个CLH锁队列的一个结点(Node)来实现锁的分配。
AQS 定义了两种资源共享方式 :独占式 (Exclusive)和共享式(Share)
独占式:只有一个线程能执行,具体的 Java 实现有 ReentrantLock。
共享式:多个线程可同时执行,具体的 Java 实现有 Semaphore和CountDownLatch。

三、锁的使用

1、synchronized

1、加锁

1、作用在方法上
修饰非静态方法(普通方法)
修饰静态方法
2、作用在代码块上(更细,更灵活)
synchronized(this|object) {...}
synchronized(类.class) {...}

2、释放锁

1、占有锁的线程执行完了该代码块,然后释放对锁的占有。
2、占有锁线程执行发生异常,此时JVM会让线程自动释放锁。
3、占有锁线程进入 WAITING 状态从而释放锁,例如在该线程中调用 wait() 方法等。

3、锁的范围

对象锁
  1、两个线程同时访问同一个对象的同步方法,会互斥;
  2、两个线程同时访问同一个对象的同步方法和非同步方法,不互斥;
  3、两个线程同时访问两个对象的同步方法,不互斥;
  4、两个线程同时访问两个对象的同步方法和非同步方法,不互斥;
类锁
  1、两个线程同时访问同一个类的静态同步方法,会互斥;
  2、两个线程同时访问不同类的静态同步方法,不互斥;
  3、两个线程同时访问同一个类的静态同步方法和静态非同步方法,不互斥;
  4、两个线程同时访问同一个类的静态同步方法和非静态同步方法,不互斥;

2、Lock

支持尝试获锁、超时获锁、中断获锁、公平锁等操作。

ReenTrantLock特点
1.独占锁
2.可重入锁。
3.支持公平和非公平,默认非公平锁
4.锁的过程是可中断的
5.可设置超时时间
6.支持多个条件变量Condition,即支持多个不同的等待队列,而synchronized只支持一个。

void lock();  加锁,如果锁已经被别人占用了,就无限等待
boolean tryLock(long timeout, TimeUnit unit) throws InterruptedException;  尝试获取锁,等待timeout时间。同时,可以响应中断
void unlock();  释放锁
boolean tryLock();  不会进行任何等待,如果能够获得锁,直接返回true,如果获取失败,就返回false
lockInterruptibly();  可以响应中断,lock方法会阻塞线程直到获取到锁
Condition newCondition();  获取与lock绑定的通信组件。
Lock lock=new ReentrantLock(false);  // 默认是false,设置为true则为公平锁。
try{
  // 加锁
  lock.lock
  逻辑代码
  }finally{
  // 解锁
  lock.unlock
}

3、volatile

1.关于内存和代码执行的概念

1.happens-before
(1).如果 A happens-before B,那 A 的执行结果将对 B 可见,且 A 的执行顺序排在 B 前。
(2).并不意味着一定要按照happens-before原则制定的顺序来执行。如果重排序之后的执行结果与按照happens-before关系来执行的结果一致,那么这种重排序并不非法。

2.指令重排序
在执行程序时,为了提高性能,编译器和处理器常常会对指令进行重排序。
一般分三种
编译器优化的重排序: 编译器在不改变单线程程序语义的前提下,重新安排语句的执行顺序;
指令级并行的重排序: 现代处理器采用了指令级并行技术来将多条指令重叠执行。如果不存在数据依赖性,处理器可以改变语句对应机器指令的执行顺序;
内存系统的重排序: 由于处理器使用缓存和读/写缓冲区,这使得加载和存储操作看上去可能是在乱序执行的。

3.as-if-serial
不管怎么重排序,单线程下的执行结果不能被改变。
编译器、runtime和处理器都必须遵守as-if-serial语义。

4.内存屏障
java编译器在生成指令系列时在适当的位置会插入内存屏障指令来禁止特定类型的处理器重排序。

2.volatile特性

1.提供可见性,任何一个线程对其的修改将立马对其他线程可见,volatile属性不会被线程缓存,始终从主存中读取。
2.修饰符适用于以下场景:某个属性被多个线程共享,其中有一个线程修改了此属性,其他线程可立即得到修改后的值。
3.属性的读写操作都是无锁的,不能替代synchronized,没有提供原子性和互斥性。因为无锁,不需要花费时间在获取锁和释放锁上。
4.只能作用于属性,我们用volatile修饰属性,这样compilers就不会对这个属性做指令重排序。
5.提供了happens-before保证,对volatile变量v的写入happens-before所有其他线程后续对v的读操作。
6.可以在单例双重检查中实现可见性和禁止指令重排序,保证安全性。
7.可以使得long和double的赋值是原子的。

3、JUC 的 tools(工具类)

1、CountDownLatch(闭锁)

同步辅助类,在完成一组正在其他线程中执行的操作之前,它支持一个或多个线程一直等待

CountDownLatch(int count):count为计数器的初始值(对应的线程数)。
countDown(): 每调用一次计数器值-1,直到count被减为0,代表所有线程全部执行完毕。
getCount():获取当前计数器的值。
await(): 等待计数器变为0,即等待所有异步线程执行完毕。
boolean await(long timeout, TimeUnit unit): 此方法与await()区别:
            1、此方法至多会等待指定的时间,超时后会自动唤醒,若 timeout 小于等于零,则不会等待
            2、boolean 类型返回值:若计数器变为零了,则返回 true;若指定的等待时间过去了,则返回 false

使用案例

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;

public class Test{
    public static void main(String[] args) throws InterruptedException {
        CountDownLatch latch = new CountDownLatch(10);

        for (int i=0; i<=10; i++) {
            new Thread(new Runnable() {
                @Override
                public void run() {
                    System.out.println(Thread.currentThread().getName() + " 运行");
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    } finally {
                        latch.countDown();
                    }
                }
            }).start();
        }
        System.out.println("等待子线程运行结束");
        latch.await();
        System.out.println("子线程运行结束");
    }
}

2、CyclicBarrier(栅栏)

同步辅助类,允许一组线程互相等待,直到到达某个公共屏障点 ,并且在释放等待线程后可以重用。

import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;

public class CyclicBarrierExample {

    public static void main(String[] args) {
        // 声明一个加法计数器,并指定触发的条件线程
        CyclicBarrier cyclicBarrier = new CyclicBarrier(10, () -> System.out.println("都已执行完."));
        for (int i = 0; i < 10 ; i++) {
            int atmp = i;
            new Thread(() -> {
                System.out.println("A-----执行完: "+ atmp);
                try {
                    // 阻塞本线程,等待其他线程
                    cyclicBarrier.await();
                } catch (InterruptedException | BrokenBarrierException e) {
                    e.printStackTrace();
                }
            }).start();
        }

        
        for (int i = 0; i < 10 ; i++) {
            int btmp = i;
            new Thread(() -> {
                System.out.println("B-----执行完: "+ btmp);
                try {
                    // 阻塞本线程,等待其他线程
                    cyclicBarrier.await();
                } catch (InterruptedException | BrokenBarrierException e) {
                    e.printStackTrace();
                }
            }).start();
        }
    }
}

3、Semaphore(信号量)

计数信号量,它的本质是一个“共享锁“。维护了一个信号量许可集。可通过调用 acquire()来获取信号量的许可;
当信号量中无可用的许可时须等待,直到有可用的许可为止。线程可以通过release()来释放它所持有的信号量许可。
用于多个线程对多个共享资源的互斥使用,另一个是用于并发线程数的控制(限流)。

import java.util.concurrent.Semaphore;

public class SemaphoreTest {

    public static void main(String[] args) {
        Semaphore semaphore = new Semaphore(2);
        for (int i = 0; i < 10; i++) {
            new Thread(() -> {
                try {
                    System.out.println(Thread.currentThread().getName() + " 线程启动,,,,,,");
                    semaphore.acquire();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    semaphore.release();
                    System.out.println(Thread.currentThread().getName() + " 线程结束。。。");
                }
            }).start();
        }
    }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/749113.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Sentinel 熔断与限流

文章目录 1 是什么&#xff1f;2 特征3 特性4 与Hystrix的区别5 两个部分6 应用6.1 依赖6.2 配置文件 7 流量配置规则7.1 直接&#xff08;默认&#xff09;7.2 关联7.3 Warm Up 预热7.4 排队等待 8 熔断降级8.1 概述RT(平均响应时间&#xff0c;秒级)异常比列(秒级)异常数(分钟…

python-cv2模块安装

1.自动安装 如果网络环境好&#xff1a; pip install opencv-python2.卸载与安装指定版本 卸载opencv pip uninstall opencv-python安装指定版本的cv 指定版本为&#xff1a;4.5.4.60 pip install opencv-python 4.5.4.603.下载安装包安装 从官网下载正确安装包安装&#x…

【Linux工具】编译器、调式器、项目自动化构建工具以及git的使用2(make/makefile和git的基本使用)

【Linux工具】编译器、调式器、项目自动化构建工具以及git的使用2&#xff08;make/makefile和git的基本使用&#xff09; 目录 【Linux工具】编译器、调式器、项目自动化构建工具以及git的使用2&#xff08;make/makefile和git的基本使用&#xff09;背景make和makefile的用法…

Proxy-Reflect使用详解

1 监听对象的操作 2 Proxy类基本使用 3 Proxy常见捕获器 4 Reflect介绍和作用 5 Reflect的基本使用 6 Reflect的receiver Proxy-监听对象属性的操作(ES5) 通过es5的defineProperty来给对象中的某个参数添加修改和获取时的响应式。 单独设置defineProperty是只能一次设置一…

AppStorage, OnboardingView 的示例

1. AppStorage 数据简单存储的实现 /// 应用程序数据简单存储 struct AppStorageBootcamp: View {//State var currentUserName: String?AppStorage("name") var currentUserName: String?var body: some View {VStack(spacing: 20) {Text(currentUserName ?? &…

Ghostscript开源PDF库中发现关键漏洞

在Linux中广泛使用的PostScript语言和PDF文件开源解释器Ghostscript被发现存在严重远程代码执行漏洞。 该漏洞被标记为CVE-2023-3664&#xff0c;CVSS v3评级为9.8&#xff0c;影响10.01.2之前的所有Ghostscript版本&#xff0c;10.01.2是三周前发布的最新版本。 据Kroll公司…

深入理解netfilter和iptables

目录 Netfilter的设计与实现 内核数据包处理流 netfilter钩子 钩子触发点 NF_HOOK宏与Netfilter裁定 回调函数与优先级 iptables 内核空间模块 xt_table的初始化 ipt_do_table() 复杂度与更新延时 用户态的表&#xff0c;链与规则 conntrack Netfilter(结合iptable…

基于C语言设计的足球信息查询系统

完整资料进入【数字空间】查看——baidu搜索"writebug" 需求分析与概要设计 2.1 项目说明 我们小组的选题主要是面向足球爱好者&#xff0c;在普通社交软件的基础之上&#xff0c;围绕足球的主题展开设计&#xff0c;以便于他们能够更好的交流相关的话题&#xff…

高效编程的捷径:HbuilderX的独特之处

目录 引言HbuilderX的功能HbuilderX的优点HbuilderX的缺点总结 HBuilderX 官网 引言 在当今科技发展日新月异的时代&#xff0c;软件开发已成为一个极富挑战性且高需求的领域。为了在竞争激烈的市场中脱颖而出&#xff0c;程序员们需要掌握一系列高效编程的技巧和工具。在这个过…

谈一谈LLM在推荐域的一些理解

作者&#xff1a;陈祖龙(葬青) 一、前言 最近大模型真的很火&#xff0c;从个人到公司&#xff0c;各行各业都在学习大模型、总结大模型和尝试应用大模型。大模型其实不是一个新的产物&#xff0c;已经在NLP发展了很多年。ChatGPT的诞生&#xff0c;经验的效果震惊了所有人&…

ES系列--es初探

一、前言 一般传统数据库&#xff0c;全文检索都实现的很鸡肋&#xff0c;因为一般也没人用数据库存文本字段。进 行全文检索需要扫描整个表&#xff0c;如果数据量大的话即使对 SQL 的语法优化&#xff0c;也收效甚微。建 立了索引&#xff0c;但是维护起来也很麻烦&#xff0…

人工智能-电脑如何像人一样思考?

发展历史 在电影如 终结者、机械公敌 中&#xff0c;机器人为什么能够像人一样思考&#xff1f;其实这就是人工智能。人工智能多方面&#xff1a;例如人脸识别系统、肺部影响CT&#xff0c;手机中的美颜、垃圾邮件拦截、自动驾驶 。 上世纪30-50年代&#xff0c;随着计算机科…

服务器如何查库磁盘情况?

查库磁盘情况 du -h --max-depth1 ./ | sort -hr

九九乘法表案例

for循环嵌套 代码&#xff1a; <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>Document</titl…

Linux5.15 Zookeeper集群 + Fafka集群

文章目录 计算机系统5G云计算第四章 LINUX Zookeeper集群 Fafka集群一、Zookeeper1.Zookeeper 概述1&#xff09;Zookeeper 定义2&#xff09;Zookeeper 工作机制3&#xff09;Zookeeper 特点4&#xff09;Zookeeper 数据结构5&#xff09;Zookeeper 应用场景6&#xff09;Zoo…

unittest与pytest自动化测试框架

引言 前面一篇文章已经介绍了python单元测试框架&#xff0c;大家平时经常使用的是unittest&#xff0c;因为它比较基础&#xff0c;并且可以进行二次开发&#xff0c;如果你的开发水平很高&#xff0c;集成开发自动化测试平台也是可以的。而这篇文章主要讲unittest与pytest的区…

大华相机接入web页面实现人脸识别

先看下效果&#xff0c;中间主视频流就是大华相机&#xff08;视频编码H.264&#xff09;&#xff0c;海康相机&#xff08;视屏编码H.265&#xff09; 前端接入视屏流代码 <!--视频流--><div id"col2"><div class"cell" style"flex: …

2023夏季营销报告新鲜出炉!(小红书平台)

夏季温度持续走高&#xff0c;大众需求也在升级。品牌如何借势和部署相关内容&#xff1f; 本期&#xff0c;千瓜推出《千瓜2023夏季营销策略数据报告&#xff08;小红书平台》&#xff08;本文非完整版报告&#xff09;&#xff0c;围绕七大行业&#xff08;美妆|个护|食品|家…

能翻译维吾尔语的软件有哪些?这几个可以用用看

能翻译维吾尔语的软件有哪些&#xff1f;在如今全球化的背景下&#xff0c;不同语言之间的沟通交流变得尤为重要。维吾尔语作为中国特有的少数民族语言之一&#xff0c;它的翻译需求日益增长。本文将介绍几款精选的维吾尔语翻译软件&#xff0c;帮助大家顺利实现跨语言沟通。 智…

creator 滑动循环展示图片 自动展示

import MyBaseView from "./MyBaseView";const { ccclass, property } cc._decorator;ccclass export default class ScrollCard extends MyBaseView {property({ tooltip: "是否自动展示" })Move_zidong: boolean true;property({ tooltip: "自动展…