首页 > 编程语言 >Java并发编程实战读书笔记(四)

Java并发编程实战读书笔记(四)

时间:2024-07-24 13:00:19浏览次数:13  
标签:ReentrantLock Java 变量 读书笔记 编程 原子 线程 操作 执行

显示锁

Lock 与 ReentrantLock

Lock 接口定义了一组抽象的加锁操作,与内置加锁机制不同,Lock 提供了一种无条件的、可轮询的、定时的以及可中断的锁获取操作,所有加锁和解锁的方法都是显式的。在 Lock 的实现中必须提供与内部锁相同的内存可见性语义,但在加锁语义、调度算法、顺序保证以及性能特性等方面可以有所不同。

ReentrantLock 实现了 Lock 接口,并提供了与 synchronized 相同的互斥性和内存可见性。在获取 ReentrantLock 时,有着与进入同步代码块相同的内存语义,在释放 ReentrantLock 时,同样有着与退出同步代码块相同的内存语义。此外,与 synchronized 一样,ReentrantLock 还提供了可重入的加锁语义。

ReentrantLock 支持在 Lock 接口中定义的所有获取锁模式,并且与 synchronized 相比,它还为处理锁的不可用性问题提供了更高的灵活性。

ReentrantLock是Java并发编程中的一个类,它实现了Lock接口,提供了与synchronized关键字类似的同步功能,但具有更高的灵活性和扩展性。ReentrantLock允许线程尝试获取锁,而不是像synchronized那样阻塞等待。此外,ReentrantLock还支持公平锁和非公平锁,以及可重入锁的功能。

使用示例:

import java.util.concurrent.locks.ReentrantLock;

public class ReentrantLockExample {
    private final ReentrantLock lock = new ReentrantLock();

    public void doSomething() {
        lock.lock(); // 获取锁
        try {
            // 临界区代码
            System.out.println("执行临界区代码");
        } finally {
            lock.unlock(); // 释放锁
        }
    }

    public static void main(String[] args) {
        ReentrantLockExample example = new ReentrantLockExample();
        example.doSomething();
    }
}

在这个示例中,我们创建了一个ReentrantLock实例,并在doSomething方法中使用lock()方法获取锁。在try块中,我们执行临界区的代码,然后在finally块中使用unlock()方法释放锁。这样可以确保即使在临界区代码抛出异常时,锁也能被正确释放。

原子变量与非阻塞同步机制

比较并交换

在大多数处理器架构,如IA32和Sparc中,采用了比较并交换(CAS)指令来实现原子操作。在其他处理器如PowerPC中,则使用一对指令——关联加载和条件存储来实现相同的功能。CAS操作包括三个操作数:内存位置V、用于比较的值A和新值B。仅当V的值等于A时,CAS会将V更新为新值B,否则不执行操作。无论操作是否成功,CAS都会返回V的原始值。这种操作被称为“比较并设置”,因为它总是返回结果。CAS是基于乐观锁原理,假设更新操作会成功,并且能在其他线程更新变量后检测到错误。

原子变量类

原子变量类是一种泛化的volatile变量,支持原子的和有条件的读-改-写操作。AtomicInteger是一个例子,它表示一个int类型的值,并提供get和set方法,这些方法在读取和写入时具有volatile变量的内存语义。此外,它还提供了原子的compareAndSet方法(如果成功执行,将具有与读取/写入volatile变量相同的内存效果),以及原子的添加、递增和递减等方法。

共有12个原子变量类,分为四组:标量类、更新器类、数组类和复合变量类。最常用的原子变量包括标量类:AtomicInteger、AtomicLong、AtomicBoolean和AtomicReference。所有这些类都支持CAS,AtomicInteger和AtomicLong还支持算术运算。

原子数组类(只支持Integer、Long和Reference版本)允许数组元素原子更新。原子数组类为数组元素提供volatile类型的访问语义,这是普通数组所不具备的。

尽管原子标量类扩展了Number类,但并没有扩展基本类型的包装类,如Integer或Long。实际上,它们不能扩展这些类,因为基本类型的包装类是不可修改的,而原子变量类是可修改的。原子变量类也没有重新定义hashCode或equals方法,因此每个实例都是不同的。与其他可变对象一样,它们也不宜用作基于散列的容器中的键值。

Java内存模型

重排序

重排序是指编译器、处理器和内存系统为了提高性能而对代码中的操作顺序进行重新排序的行为。在多线程程序中,这种重排序可能会导致竞态条件和原子性故障,从而使得程序的执行结果与预期不符。

在Java中,JMM(Java Memory Model)允许不同线程看到的操作执行顺序是不同的,这增加了在缺乏同步的情况下推断操作执行顺序的复杂性。重排序的各种原因,如编译器优化、缓存一致性协议等,都可能导致操作延迟或看似乱序执行。

内存模型简介

Java内存模型(JMM)是通过各种操作来定义的,包括对变量的读/写操作、监视器的加锁和释放操作,以及线程的启动和合并操作。JMM为程序中的所有操作定义了一个偏序关系,称为Happens-Before。要确保执行操作B的线程看到操作A的结果(无论A和B是否在同一个线程中执行),在A和B之间必须满足Happens-Before关系。如果两个操作之间缺乏Happens-Before关系,那么JVM可以对它们任意地重排序。

当一个变量被多个线程读取并且至少被一个线程写入时,如果在读操作和写操作之间没有依照Happens-Before来排序,那么就会产生数据竞争问题。在正确同步的程序中不存在数据竞争,并会表现出串行一致性,这意味着程序中的所有操作都会按照一种固定的和全局的顺序执行。

Happens-Before的规则包括:

  1. 程序顺序规则:如果程序中操作A在操作B之前,那么在线程中A操作将在B操作之前执行。
  2. 监视器锁规则:在监视器锁上的解锁操作必须在同一个监视器锁上的加锁操作之前执行。
  3. volatile变量规则:对volatile变量的写入操作必须在对该变量的读操作之前执行。
  4. 线程启动规则:在线程上对Thread.start的调用必须在该线程中执行任何操作之前执行。
  5. 线程结束规则:线程中的任何操作都必须在其他线程检测到该线程已经结束之前执行,或者从Thread.join中成功返回,或者在调用Thread.isAlive时返回false。
  6. 中断规则:当一个线程在另一个线程上调用interrupt时,必须在被中断线程检测到interrupt调用之前执行(通过抛出InterruptedException,或者调用isInterrupted和interrupted)。
  7. 终结器规则:对象的构造函数必须在启动该对象的终结器之前执行完成。
  8. 传递性:如果操作A在操作B之前执行,并且操作B在操作C之前执行,那么操作A必须在操作C之前执行。

虽然这些操作只满足偏序关系,但同步操作,如锁的获取与释放等操作,以及volatile变量的读取与写入操作,都满足全序关系。因此,在描述Happens-Before关系时,就可以使用“后续的锁获取操作”和“后续的volatile变量读取操作”等表达术语。

标签:ReentrantLock,Java,变量,读书笔记,编程,原子,线程,操作,执行
From: https://blog.csdn.net/Luck_gun/article/details/140648750

相关文章

  • Java并发编程实战读书笔记(二)
    对象的组合在设计线程安全的类时,确保数据的一致性和防止数据竞争是至关重要的。这通常涉及三个基本要素:确定构成对象状态的所有变量,明确约束这些状态变量的不变性条件,以及建立管理对象状态并发访问的策略。要确定构成对象状态的所有变量相对简单,但需注意状态应封装在对象......
  • Java中的优先级队列(PriorityQueue)(如果想知道Java中有关优先级队列的知识点,那么只看这
        前言:优先级队列(PriorityQueue)是一种抽象数据类型,其中每个元素都关联有一个优先级,元素按照优先级顺序进行处理。✨✨✨这里是秋刀鱼不做梦的BLOG✨✨✨想要了解更多内容可以访问我的主页秋刀鱼不做梦-CSDN博客先让我们看一下本文大致的讲解内容:目录1.优......
  • 基于Java+SpringBoot+Vue的卓越导师双选系统的设计与开发(源码+lw+部署文档+讲解等)
    文章目录前言项目背景介绍技术栈后端框架SpringBoot前端框架Vue数据库MySQL(MyStructuredQueryLanguage)具体实现截图详细视频演示系统测试系统测试目的系统功能测试系统测试结论代码参考数据库参考源码获取前言......
  • 模块2 面向对象编程初级 --- 第六章:创建对象
    第六章创建对象主要知识点:1、类的实例化2、构造方法3、对象的使用4、对象的清除学习目标:根据定义的类进行实例化,并且运用对象编写代码完成一定的功能。本章对类进行实例化,生成类的对象,利用对象开始软件的设计过程,掌握对象的使用方法。6.1创建对象概......
  • java内部类详解
    24-07-22java内部类目录24-07-22java内部类什么是内部类内部类的分类局部内部类匿名内部类AnonymousClass(重点)成员内部类静态内部类什么是内部类简单来说,一个类的内部嵌套了另一个类结构,被嵌套的结构被称为内部类,嵌套其他类的类我们称为外部类。在开始学习之前,我们先来回想......
  • Druid出现DruidDataSource - recyle error - recyle error java.lang.InterruptedExce
    原文链接: https://www.cnblogs.com/zhoading/p/14040939.htmlhttps://www.cnblogs.com/lingyejun/p/9064114.html 一、问题回顾线上的代码之前运行的都很平稳,突然就出现了一个很奇怪的问题,看错误信息是第三方框架Druid报出来了,连接池回收连接时出现的问题。1234......
  • Java面试题总结(持续更新)
    1、this关键字和super关键字的区别及联系this关键字用在本类中。在类的内部,可以在任何方法中使用this引用当前对象。this关键字是用来解决全局变量和局部变量之间的冲突。this()可以调用同类中重载的构造方法,并且需要放在第一行。super关键字用在子类中。在子类中可以通......
  • JavaScript中的new map()和new set()使用详细(new map()和new set()的区别)
    简介:newMap():在JavaScript中,newMap()用于创建一个新的Map对象。Map对象是一种键值对的集合,其中的键是唯一的,值可以重复。newSet():在JavaScript中,newSet()是用来创建一个新的Set对象的语法。Set对象是一种集合,其中的值是唯一的,没有重复的值。newSet()可以用......
  • Aspose项目实战!pdf、cells for java
    Aspose实战使用:Excel与PDF转换工具类在这篇博客中,我将分享如何使用Aspose库来实现Excel文件与PDF文件之间的转换。我会重点分析一个工具类AsposeOfficeUtil,这个类封装了多个与Excel和PDF相关的操作方法,帮助开发者高效地进行文件转换和数据处理。此外,还将提......
  • python_网络编程_socket
    一、网络编程的基本概念通信协议:internet协议,任何私有网络支持此协议,就可以接入互联网二、七层协议与四层协议从下到上分别是:物理层、数据链路层、网络层、传输层、会话层、表示层、应用层三、掌握TCP、IP协议ip协议是整个TCP、IP协议族的核心IP地址就是会联网上计算......