并发编程
文章平均质量分 88
以理论为基础,实战为线索,逐步深入Java并发编程,提升工程化编程能力和思维能力。
瞎胡扯
这个作者很懒,什么都没留下…
展开
-
@Transactional 事务加了 锁 为什么还有并发问题?
这个手动开启事务,需要每个方法都需要实现,这个也是比较繁琐,这种方式可以抽象出一个公共类,统一来实现事务的处理。方法一和方法二的效率都比较低,另一种方式可以把,可以把需要 把需要并发控制的业务,单独抽离出来,进行事务控制操作。由上图可以看出,当线程1 释放了锁,还未提交事务之前,线程2 已经获取锁并提前提交了事务,从而导致了并发的问题。不允许事务并发执行,而必须串行化执行,最安全,不可能出现更新、脏读、不可重复读、幻读,但是效率最低。注解的方法 添加 AOP切面来时先事务管理的。最大范围也就是方法级别的。原创 2022-10-11 15:44:10 · 3862 阅读 · 1 评论 -
HashMap 插入、扩容、查询、删除原理源码分析
一、前言上一章我们主要介绍了HashMap中的数据结构、hash 算法等知识点,除了这些,HashMap中还有 插入、删除、查找等基本的功能。在面试中可能还会经常问到 HashMap 的扩容是怎么实现的。接下来就通过源码分析一下这些的实现机制。二、HashMap 的插入操作。2.1、put操作源码分析 public V put(K key, V value) { return putVal(hash(key), key, value, false, true); }原创 2021-01-14 10:16:03 · 706 阅读 · 0 评论 -
为什么说 HashMap 不是线程安全的
前言:我们都知道HashMap是线程不安全的,在多线程环境中不建议使用,但是其线程不安全主要体现在什么地方呢,本文将对该问题进行解密。1.jdk1.7中的HashMap在jdk1.8中对HashMap做了很多优化,这里先分析在jdk1.7中的问题,相信大家都知道在jdk1.7多线程环境下HashMap容易出现死循环,这里我们先用代码来模拟出现死循环的情况:public class HashMapTest { public static void main(String[] args)转载 2021-01-12 20:38:00 · 660 阅读 · 0 评论 -
为啥 HashMap 初始值是 2 的 n 次幂?
集合是Java开发日常开发中经常会使用到的,而作为一种典型的K-V结构的数据结构,HashMap对于Java开发者一定不陌生。在日常开发中,我们经常会像如下方式以下创建一个HashMap:Map<String, String> map = new HashMap<String, String>();但是,大家有没有想过,上面的代码中,我们并没有给HashMap指定容量,那么,这时候一个新创建的HashMap的默认容量是多少呢?为什么呢?本文就来分析下这个问题。转载 2021-01-12 17:39:15 · 597 阅读 · 0 评论 -
彻底搞懂 HashMap 底层原理
HashMap绝对是最常用,也是面试时最常问的集合之一,只有把所有的要点都要烂熟于心,再面大厂时才能胸有成竹,应对自如。接下来就带你慢慢揭开 HashMap 面纱。一、提出问题学习一个知识点,最好的方式就是带着问题学习,那么我们首先抛出几个面试中常见的问题,然后带着这些问题我们一点一点的剖析HashMap的原理。HashMap 的底层数据结构?HashMap 的存取原理?Java7 和 Java8的区别?为啥会线程不安全?有什么线程安全的类代替么?默认初始化大小是多少?为啥是这么多?为啥大原创 2021-01-12 16:39:15 · 274 阅读 · 0 评论 -
并发编程 — ThreadPoolExecutor 线程池实现原理
一、线程池的概念线程池(Thread Pool)是一种基于池化思想管理线程的工具。类似于我们连接数据库的连接池。线程池解决的核心问题就是资源管理问题。在并发环境下,系统不能够确定在任意时刻中,有多少任务需要执行,有多少资源需要投入。这种不确定性将带来以下若干问题:创建一个线程,却需要调用操作系统内核的 API,然后操作系统要为线程分配一系列的资源,创建成本很高 频繁申请/销毁资源和调度资源,将带来额外的消耗,可能会非常巨大。 对资源无限申请缺少抑制手段,易引发系统资源耗尽的风险。 系统无法合原创 2021-01-08 17:04:29 · 1855 阅读 · 0 评论 -
并发编程 — Condition 使用及原理详解
一、概述Condition本身也是一个接口,其功能和Object类中 wait/notify类似。Object中的 wait 和 notify方法需要与synchronized关键字配合使用,可实现线程间的等待/通知功能。Condition 接口也是提供了类似的功能,但是需要与Lock配合使用,可实现等待/通知模式。二、Condition 接口与示例Condition 定义了等待 / 通知两种类型的方法,当前线程调用这些方法时,需要提前获取Condition 对象关联的锁。Condition .原创 2020-12-19 21:54:58 · 789 阅读 · 5 评论 -
并发编程 — ReadWriteLock 读写锁实现原理详解
目录一、概述二、ReentrantReadWriteLock 实现原理1、ReentrantReadWriteLock 类层次结构2、使用范式3、读写锁的基本实现原理4、写锁的获取与释放5、读锁的获取与释放三、总结一、概述读写锁与排他锁(独占锁)不同的是,读写锁在同一时刻可以允许多个读线程方法,但是在写线程访问时,所有的读线程和其它写线程均被阻塞。读写锁维护了一对锁,一个读锁和一个写锁,通过分离读锁和写锁,是的并发性相比一般的排它锁有了很大的提升。一般情况下,读写原创 2020-12-19 18:40:03 · 5661 阅读 · 2 评论 -
并发编程 — ReentrantLock 原理详解
一、概述ReentrantLock 是JDK1.5 引入的J.U.C包中的一个比较重要的可重入的、互斥的并且支持公平和非公平的锁,其实现了 Lock接口,其内部是通过 AQS + CAS 原理实现的。其具备 synchronized 关键字加锁的所有功能,并且还具备一些 synchronized 锁不具备的功能,比如:尝试拿锁,支持中断、支持超时等待等。二、什么是可重入锁可重入锁,顾明思议,就是支持重进入的锁,它表示该锁能够支持同一个线程对资源的重复加锁。通俗来讲就是 如果线程 A 已经获取的锁原创 2020-12-19 15:14:24 · 437 阅读 · 1 评论 -
idea 快捷键
1、查看类的继承关系 通过 Ctrl + Shift + Alt + U 或者 Ctrl + Alt + U 快捷键可用快速查看 类的继承关系图,两者的区别是,前者在当前打开一个页签,后者是打开一个小窗口。如下所示Ctrl + Shift + Alt + U:Ctrl + Alt + U 2、查看接口的实现类 Ctrl + Alt + B 或者 Ctl + H, 两者的区别是 前置打开一个小窗口,后者会在右侧栏中打开Ctrl + Alt + B...原创 2020-12-19 10:47:34 · 757 阅读 · 1 评论 -
并发编程 — Lock 接口说明
一、概述在Java1.5版本以前,我们开发多线程程序只能通过关键字synchronized进行共享资源的同步、临界值的控制,虽然随着版本的不断升级,JDK对synchronized关键字的性能优化工作一直都没有停止过,但是synchronized在使用的过程中还是存在着比较多的缺陷和不足,比如:等待synchronized 锁的线程无法被中断,无法设置超时时间,无法获取当前有多少线程被阻塞。在JDK 1.5 版本以后引入了 Lock 显示锁,Lock 锁不但具备 synchronized 关键字提供的所有原创 2020-12-19 10:17:59 · 182 阅读 · 1 评论 -
并发编程之CyclicBarrier详解
一、概述CyclicBarrier(循环屏障),它是一个同步助手工具,它允许多个线程在执行完相应的操作之后彼此等待共同到达一个障点(barrier point)。CyclicBarrier 也非常适合用于某个串行化任务被分拆成若干个并行执行的子任务,当所有的子任务都执行结束之后再继续接下来的工作。CyclicBarrier 使一定数量的线程反复地在屏障位置处汇集。当线程到达屏障位置时将调用 await 方法,这个方法将会阻塞,直到所有线程都到达屏障位置,当所有线程都到达屏障位置,那么屏障将打开,此时原创 2020-12-15 21:06:21 · 577 阅读 · 0 评论 -
并发编程 — CountDownLatch 详解
一、概述类 CountDownLatch 是一个同步功能的辅助类,使用效果是给定一个计数,当使用这个CountDownLatch类的线程判断计数不为0时,则呈wait状态,如果为0时则继续运行。实现等待与继续运行的效果分别需要使用await()和countDown()方法来进行。调用await()方法时判断计数是否为0,如果不为0则呈等待状态。其他线程可以调用countDown()方法将计数减1,当计数减到为0时,呈等待的线程继续运行。而方法getCount()就是获得当前的计数个数。二、使用场景原创 2020-12-14 22:59:45 · 866 阅读 · 0 评论 -
并发编程 — AQS 原理 详解
一、概述AQS 全称为AbstractQueuedSynchronizer (队列同步器),这个类是其他许多同步类的基类,它是使用一个 volatile 修饰 int 类型成员变量表示某种状态(如:ReentrantLock用它来表示所有者线程已经重复获取锁的次数,Semaphore用它来表示剩余的许可数量,FutureTask用它来表示任务的状态),通过内置一个虚拟的 FIFO 队列来完成获取资源的线程的排队等待工作。在J.U.C 包中很多同步器都是基于 AQS 构建的,在基于 AQS 构建的同步器.原创 2020-12-13 14:38:37 · 462 阅读 · 0 评论 -
并发编程 — AtomicXXXFieldUpdater 详解
AtomicXXXFieldUpdater 其中包含AtomicIntegerFieldUpdater、AtomicLongFieldUpdater 和AtomicReferenceFieldUpdater 三个类。1、为什么需要AtomicXXXFieldUpdater如果一个类是自己编写的,则可以在编写的时候把成员变量定义为Atomic类型。但如果是一个已经有的类,在不能更改其源代码的情况下,要想实现对其成员变量的原子操作,就需要AtomicIntegerFieldUpdater、Atomi..原创 2020-12-12 16:37:02 · 501 阅读 · 0 评论 -
并发编程 — AtomicMarkableReference 详解
AtomicMarkableReference 与AtomicStampedReference 一样也可以解决 ABA的问题,两者唯一的区别是,AtomicStampedReference 是通过 int 类型的版本号,而AtomicMarkableReference 是通过 boolean 型的标识来判断数据是否有更改过。既然有了AtomicStampedReference 为啥还需要再提供AtomicMarkableReference 呢,在现实业务场景中,不关心引用变量被修改了几次,只是单...原创 2020-12-12 15:38:49 · 2609 阅读 · 2 评论 -
并发编程 — AtomicStampedReference 详解
AtomicInteger、AtomicBoolean、AtomicLong、AtomicReference 这些原子类型,它们无一例外都采用了基于 volatile 关键字 +CAS 算法无锁的操作方式来确保共享数据在多线程操作下的线程安全性。volatile关键字保证了线程间的可见性,当某线程操作了被volatile关键字修饰的变量,其他线程可以立即看到该共享变量的变化。 CAS算法,即对比交换算法,是由UNSAFE提供的,实质上是通过操作CPU指令来得到保证的。CAS算法提供了一种快速失败的方式原创 2020-12-12 15:21:00 · 8389 阅读 · 0 评论 -
并发编程 — 原子类 AtomicReference 详解
通过对 AtomicInteger、AtomicBoolean 和 AtomicLong 分析我们发现,这三个原子类只能对单个变量进行原子操作,那么我们如果要对多个变量进行原子操作,这三个类就无法实现了。那如果要进行多个变量进行原子操作呢?操作方式就是,先把 多个变量封装成一个类,然后通过 AtomicReference 进行操作。众所周知,对象的引用其实是一个4字节的数字,代表着在JVM堆内存中的引用地址,对一个4字节数字的读取操作和写入操作本身就是原子性的,通常情况下,我们对对象引用的操作一般都是获原创 2020-12-12 12:49:00 · 9409 阅读 · 2 评论 -
并发编程 — 原子类 AtomicLong 详解
与AtomicInteger非常类似,AtomicLong提供了原子性操作long类型数据的解决方案,AtomicLong所提供的原子性方法在使用习惯上也与AtomicInteger非常一致。我们不再详细解释每一个方法如何使用,可以参考 《AtomicInteger》 。AtomicInteger类中最为关键的方法为compareAndSwapInt,同样,在AtomicLong类中也提供了类似的方法compareAndSwapLong,但是该方法要比compareAndSwapInt复杂很多。我们知道原创 2020-12-12 10:56:39 · 1203 阅读 · 0 评论 -
并发编程 — AtomicBoolean 详解
AtomicBoolean提供了一种原子性地读写布尔类型变量的解决方案,通常情况下,该类将被用于原子性地更新状态标识位,比如flag。1、AtomicBoolean 的基本用法AtomicBoolean类比较简单,其内部实现原理与AtomicInteger类似1.1、AtomicBoolean 的创建AtomicBoolean 也提供了个有参,无参两个构造方法。 //无参 构造方法默认值为 false AtomicBoolean flag = new AtomicBoolean().原创 2020-12-12 10:17:18 · 19077 阅读 · 1 评论 -
并发编程 — 原子类 AtomicInteger 详解
一、AtomicInteger的基本用法AtomicInteger与int的引用类型Integer继承Number类一样,AtomicInteger也是Number类的一个子类,除此之外,AtomicInteger还提供了很多原子性的操作方法。在AtomicInteger的内部有一个被volatile关键字修饰的成员变量value,实际上,AtomicInteger所提供的所有方法主要都是针对该变量value进行的操作。1、创建AtomicIntegerAtomicInteger类提供了Atom原创 2020-12-12 09:58:24 · 1851 阅读 · 0 评论 -
并发编程 — CAS 原理详解
在JDK1.5引入的 J.U.C包中的原子类以及Lock等都是基于 volatile 关键结合 CAS 操作实现的,为了能够搞明白 原子类以及 Lock锁的原理首先要了解 volatile 原理以及 CAS原理,上篇文章我们说了volatile关键字,这篇咱们就聊聊 什么是 CAS 操作。悲观锁与乐观锁在说CAS操作之前咱们先说一下什么是悲观锁和乐观锁。悲观锁悲观锁就是总是加锁最坏的情况,所以每次去拿数据时都认为别人会修改,所以每次操作共享数据时都会上锁,这样当别人需要访问共享数据时,就必须原创 2020-12-11 18:49:06 · 287 阅读 · 0 评论 -
并发编程 — 深入解析 volatile 关键字
在上篇文章《缓存一致性问题》,今天就聊聊 volatile 关键字。volatile 关键字规则Java内存模型对volatile关键字定义了一些特殊的访问规则,当一个变量被volatile修饰后,它将具备两种特性,或者说volatile具有下列两层语义:第一、保证了不同线程对这个变量进行读取时的可见性, 即一个线程修改了某个变量的值, 这新值对其他线程来说是立即可见的。 (volatile 解决了线程间共享变量的可见性问题)。 第二、禁止进行指令重排序, 阻止编译器对代码的优化。保证可见性原创 2020-12-11 15:20:35 · 155 阅读 · 0 评论 -
并发编程 — 缓存一致性问题
一、机器硬件CPU在计算机中,所有的运算操作都是有CPU的寄存器来完成的,CPU指令的执行过程需要涉及数据的读写操作,CPU所能访问的所有数据只能是计算机的主存,虽然CPU的发展频率不断提升,但是内存在访问速度上并没有多大的突破,因此CPU的处理速度和内存的访问速度之间的差距越来越大。1、CPU 缓存由于CPU和内存的速度严重不对等,如果CPU直接访问主内存,那么严重拖了CPU的后腿,于是为了提供CPU的吞吐量,于是就在CPU和主内存之间增加了缓存的设计,现在缓存的数量都增加到了 3 级,最靠近原创 2020-12-10 23:42:18 · 296 阅读 · 0 评论 -
并发编程— wait 与 notify 为什么是 Object 的成员方法?
1、为什么必须和 synchronized 一起使用在 Java 里面, wait() 和 notify() 是 Object 的成员函数,是基础中的基础。为什么Java 要把 wait() 和 notiry() 放在如此基础的类里面,而不作为 Thread 类的成员函数,或者其他类的函数呢?在回答这个问题之前,我们先聊聊 为什么 wait() 和 notify() 必须和 synchronized 一起使用?看下面代码:public class SynchronizedDemo3 { p原创 2020-12-10 19:07:53 · 828 阅读 · 2 评论 -
并发编程—synchronized 关键字 隐式锁
在上一篇中我们讲解了管程的概念,在Java中有两种实现管程的方式,分别是 synchronized 关键字和 Lock 接口,那么今天我们就先讲讲 synchronized 关键字的使用方法。通过上一章我们知道管程主要解决并发编程中的两大问题,互斥 和 同步。那么 synchronized 关键字是如何实现的呢?一、什么是 synchronizedsynchronized关键字提供了一种锁机制,能够保证共享变量的互斥访问,从而防止数据不一致性问题的出现,synchronized 又称为对象锁,重原创 2020-12-10 18:25:47 · 155 阅读 · 1 评论 -
并发编程—万能钥匙“管程”
并发编程技术相关的理论和技术可谓纷繁复杂,那么有没有一种核心技术可以很方便的解决我们的并发问题呢?答案是有的,那就是管程。一、什么是管程解决并发问题最早是基于信号量的,后来又提出了管程技术,管程和信号量是等价的,所谓等价就是用管程能够实现信号量,也能用信号量实现管程。但是管程更容易使用,所有Java选择了管程。所谓管程,是指管理共享变量以及对共享变量的操作过程,让他们支持并发。翻译为 Java 领域的语言,就是管理类的成员变量和成员方法,让这个类是线程安全的。管程,对应的英文是 Monitor,原创 2020-12-10 17:36:18 · 236 阅读 · 0 评论 -
并发编程—安全性、活跃性以及性能问题
目录一、安全性问题1、什么是线程安全呢?二、活跃性问题1、活锁2、饥饿三、性能问题四、总结并发编程中我们需要注意的问题有很多,主要有安全性问题、活跃性问题和性能问题。接下来我们就一 一解析一下这三个问题。一、安全性问题1、什么是线程安全呢?线程安全的本质就是正确性,而正确性的含义就是程序按照我们期望的执行,在《并发编程—可见性、原子性、有序性 BUG源头》一章中我们介绍了很多诡异性的问题,他们都是出乎我们预料的,没有按照我们的期望执行。我们知道导致 程序出现问题的原创 2020-12-10 16:49:19 · 426 阅读 · 1 评论 -
并发编程—等待-通知
有上一篇文章我们知道,在破坏占用且等待条件的时候,如果两个资源有一个被占用后,用的是死循环的方式来循环等待,代码如下所示://死循环的方式 while (!allocator.apply(this, tar)) ;如果说apply()操作耗时非常短,而且并发冲突量不大时,可以使用这个方案。如果apply()操作非常耗时,或者并发冲突量非常大的时候,这种循环等待的方案就不适用了,因为这种场景下,可能要循环上万次才能获取到锁,相当耗CPU。其实在这种场景下,做好的方案是:如果线程要求的条件不满足原创 2020-12-10 15:57:08 · 164 阅读 · 0 评论 -
并发编程—死锁了,怎么办?
上一篇文章中提到了如果多个资源之间不存在关系时,尽量使用细粒度的锁,但是在实际应用中,使用细粒度的锁有时会付出惨重代价的,这个代价就是可能造成可怕的“死锁”。那么什么是死锁呢?死锁是指一组互相竞争资源的线程因为互相等待,导致“永久”阻塞的现象。如何预防死锁并发程序一旦死锁,一般没有特别好的方法,很多时候我们只能重启应用。因此,解决死锁问题做好的办法还是规避死锁。那如何避免死锁呢?要避免死锁就需要先分析死锁发生的条件,只有以下四个条件都发生时才会出现死锁:互斥,共享资源X和Y只能..原创 2020-12-10 15:55:54 · 189 阅读 · 0 评论 -
并发编程—如何使用一把锁保护多个资源?
目录一、保护没有关联关系的多个资源二、保护有关联关系的多个资源三、正确使用锁的姿势四、总结上篇文章中,我们提到了受保护资源和锁之间合理的关系应该是N:1的关系,也就是说可以用一把锁来保护多个资源,而不能用多把锁来保护一个资源。那么如何使用一把锁保护多个资源呢?一、保护没有关联关系的多个资源比如如下所示的代码,在Account类中有余额 balance 和 密码 password 两个属性,而修改密码和取款两个是不相干的操作,在转账时,可以修改密码,在修改密码时也,也可以转账。当然原创 2020-11-30 16:40:43 · 396 阅读 · 0 评论 -
并发编程—原子性问题是怎么解决的
一、概述在《并发编程—可见性、原子性、有序性 BUG源头》一章中我们提到了,一个或者多个操作在CPU执行过程中不被中断的特性称之为“原子性”,而其中原子性的源头又是线程切换,如果能够禁止线程切换那不就可以解决原子性问题了吗?而操作系统做线程切换是依赖CPU中断的,所以禁止CPU发生中断就可以禁止线程切换了。在早期的单核CPU时代,这个方案是可行的,而且也有很多应用案例,但在多核场景就不适用了。在单核CPU场景下,同一时刻只有一个线程执行,禁止CPU中断,意味着操作系统不会重新调度线程,也就禁止了线程原创 2020-11-27 15:44:19 · 450 阅读 · 0 评论 -
并发编程—如何解决可见性和有序性问题
在上一篇并发编程之BUG源头我们介绍了导致并发编程出现诡异问题的三大源头,即:缓存导致了可见性问题,线程切换带来了原子性问题,编译优带来了有序性问题,这三个Bug源头在所有的编程语言中都会遇到,那么今天就聊聊 Java是通过什么技术解决的。Java中解决可见性和有序性问题的主角当属 Java内存模型了。说到Java内存模型,在很多面试中都会问到,是一个热门考点,也是一个程序员并发水平的具体体现。只有掌握了Java内存模型,才能在解决问题时慧眼如炬。什么是Java内存模型我们知道,导致可见性的原因原创 2020-11-26 11:08:38 · 478 阅读 · 0 评论 -
并发编程—可见性、原子性、有序性 BUG源头
如果细心观察,你会发现,不管哪一门编程语言,并发类的知识都是在高级篇里。也就是说,并发编程这块知识对于程序员来说,是比较进阶的知识。因为并发编程的知识会涉及到很多底层的知识,比如操作系统,有的可能还涉及到硬件CPU的知识。我们都知道,编写一个正确的并发程序是一件极其困难的事情,并发程序的问题往往很诡异的出现,让后有诡异的消失,很难跟踪,又极难复现,让很多人为之抓狂。要想能够速度而又精准地解决“并发”类的疑难杂症,就要搞清楚导致这些问题的本质、追本溯源深入分析这些Bug的源头。一、幕后的故事随着C原创 2020-11-25 18:40:35 · 496 阅读 · 0 评论 -
并发编程—学习攻略
并发编程的知识点多且又杂,并发中的概念很多并且技术点也很凌乱,看了很多有关并发的书籍,大牛写的博客等,不能很好的掌握学习知识的方法和技巧,花费了时间和精力,还是不能很好地掌握并发的知识,但那么如何才能学好并发编程呢?其实也很简单,只要从两个方面一下就可以突破了。一个是“跳出来、看全景”,一个是“钻进去,看本质”。一、跳出来,看全景学习知识最忌讳的就是“盲人摸象”,只看局部,而没有先看到全局。有些人还没有把最基本的什么是并发编程,并发编程的本质是什么,上来就撸源码,看的是晕头转向,从而感慨一句“并原创 2020-11-25 16:01:12 · 191 阅读 · 0 评论 -
并发编程—带你深入理解 synchronized 原理
1、基本使用1.1、 synchronized的主要作用有如下三方面1、原子性:确保线程互斥的访问代码块;2、可见性:保证共享变量的修改能够及时可见,其实是通过Java内存模型中的“对一个变量unlock操作之前,必须要同步到主内存中;如果对一个变量进行lock操作,则将会清空工作内存中此变量的值,在执行引擎使用此变量钱,需要重新从主内存中load操作或者assign操作初始化变量”来保证;3、有序性:有效解决重排序问题,即“一个unlock操作先行发生(happen-before)于后面对同一个原创 2020-09-16 15:16:09 · 359 阅读 · 0 评论