
02JUC
文章平均质量分 94
JUC专栏
CodingW丨编程之路
打造一个懂技术+懂业务+懂管理+略懂英语的社区
→ 高级后端架构师
能够独立负责项目0~1展开到落地
归纳为一句话
目标就是 技术 + 业务 + 管理 + 英语 -> 实现自我价值 -> 带领团队能够开展0~1项目到落地
展开
-
01基本介绍篇(D1_认识多线程)
认识多线程原创 2024-08-27 09:15:12 · 1026 阅读 · 0 评论 -
01基本介绍篇(D2_多线程问题)
在多线程编程中,线程个数一般都大于 CPU 个数,而每个 CPU 同一时刻只能被一个线程使用,为了让用户感觉多个线程是在同时执行的,CPU资源的分配采用了时间片轮转的策略,也就是给每个线程分配一个时间片,线程在时间片内占用 CPU 执行任务。当前线程使用完时间片后,就会处于就绪状态并让出CPU让其他线程占用,这就是上下文切换,从当前线程的上下文切换到了其他线程。那么就有一个问题,让出CPU 的线程等下次轮到自己占有CPU时如何知道自己之前运行到哪里了?原创 2025-01-14 23:10:27 · 1662 阅读 · 0 评论 -
01基本介绍篇(D3_进程-线程-协程-纤程-管程)
我们知道,除了初始化进程 init (PID=1) 以外,所有的进程都不是完全孤立存在的,它有其父进程和兄弟进程。所谓僵尸进程,就是当子进程退出时,父进程尚未结束,而父进程又没有对已经结束的子进程进行回收。此时,这样的子进程就成了僵尸进程。考虑另外一种情况,当一个进程使用 fork() 产生了一个子进程,当子进程尚未结束时,父进程已经退出,则此时的子进程就成了孤儿进程,孤儿进程会被 init 进程收养,当孤儿进程退出时,由init 进程负责对其进行回收。原创 2024-08-28 09:15:29 · 1279 阅读 · 0 评论 -
01基本介绍篇(D4_线程实现方式)
自定义线程 ThreadDemo,ThreadDemo类继承了Thread类,并重写了 run() 方法。在 main 函数里面创建了一个 MyThread 的实例,然后调用该实例的 start 方法启动了线程。当创建完 thread 对象后该线程并没有被启动执行,直到调用了start方法后才真正启动了线程,也就是说在线程未调用 start()方法时,前面所走的程序还是属于单线程程序,那么 start 就意味着开启多线程!原创 2024-08-28 10:25:48 · 1023 阅读 · 0 评论 -
01基本介绍篇(D5_线程相关API)
Java 中的 Object 类是所有类的父类,鉴于继承机制,Java 把所有类都需要的方法放到了Object类里面,join()方法是Thread类中的一个方法,可以由线程对象调用。在线程操作中,可以使用join()方法让一个线程强制运行,线程强制运行期间,其他线程无法运行,必须等待此线程完成之后才可以继续执行,即等待线程执行终止。在当前线程调用join()方法时并不需要提前自己写同步代码块来获取对象锁,调用了join()方法也不会释放当前线程所持有的对象锁。原创 2024-08-29 09:15:04 · 869 阅读 · 0 评论 -
02锁机制篇(D1_常见锁)
在多核时代中,多线程、多进程的程序虽然大大提高了系统资源的利用率以及系统的吞吐量,但并发执行也带来了新的一系列问题:死锁、活锁与锁饥饿。死锁、活锁与锁饥饿都是程序运行过程中的一种状态,而其中死锁与活锁状态在进程中也是可能存在这种情况的,以下就是对死锁、活锁、锁机饿的介绍。死锁是指两个或两个以上的线程在执行过程中,因争夺资源而造成的互相等待的现象,在无外力作用的情况下,这些线程会一直相互等待而无法继续运行下去。原创 2024-08-29 09:27:32 · 2118 阅读 · 0 评论 -
02锁机制篇(D2_锁优化)
具体的解决思路是,A 和 B 同时将猪肉脯(id = 1 下面都说是 id = 1)的数据查出来,然后 A 先。买,A 将id = 1 和 version = 0 作为条件进行数据更新,即将数量减一,并且将版本号加一。我们经常使用的数据库是 mysql,mysql 中最常用的引擎是 Innodb,Innodb 默认使用的是行。2、事务 A 进行购买更新数据,然后再查询更新后的数据。其更新到表中时,会将之前取出的版本 v1 与数据中最新的版本 v2 相对比,如果 v1 = v2 ,那么。原创 2024-08-30 08:56:18 · 1392 阅读 · 0 评论 -
02锁机制篇(D3_锁相关类 - D1_LockSupport(锁基类))
JDK 中的 rt.jar 包里面的 LockSupport 是个工具类,它的主要作用是挂起和唤醒线程,该工具类是创建锁和其他同步类的基础。LockSupport类与每个使用它的线程都会关联一个许可证,在默认情况下调用LockSupport 类的方法的线程是不持有许可证的。LockSupport 是使用 Unsafe 类实现的,下面详细介绍一下。LockSupport是concurrent包中的一个线程阻塞工具类,所有的方法都是静态方法,不提供构造,可以让。原创 2024-09-01 08:42:03 · 1467 阅读 · 0 评论 -
02锁机制篇(D3_锁相关类 - D2_ReentrantLock(独占锁))
java.util.concurrent 包中的大多数同步器实现都是围绕着共同的基础行为,如等待队列、条件队列、独占获取、共享获取等,而这些行为的抽象就是基于 AbstractQueuedSynchronizer(简称AQS)实现的,AQS 是一个抽象同步框架,可用来实现一个依赖状态的同步器。管程:一般使用 MESA,其中有两个队列:① 同步等待队列(即上图的入口等待队列,获取锁有关,获取锁失败的线程会加入此队列),② 条件等待队列(阻塞唤醒机制)原创 2024-08-31 20:21:56 · 1046 阅读 · 0 评论 -
02锁机制篇(D3_锁相关类 - D3_ReentrantReadWriteLock(读写锁))
解决线程安全问题使用 ReentrantLock 就可以,但是 ReentrantLock 是独占锁,某时只有一个线程可以获取该锁,而实际中会有写少读多的场景,显然 ReentrantLock满足不了这个需求,所以ReentrantReadWriteLock 应运而生。ReentrantReadWriteLock采用读写分离的策略,允许多个线程可以同时获取读锁。独占锁:在独占锁模式下,每次只能有一个线程能持有锁,ReentrantLock就是以独占方式实现的互斥锁。原创 2024-08-31 20:23:27 · 1313 阅读 · 0 评论 -
02锁机制篇(D3_锁相关类 - D4_StampedLock(JDK8新增))
StampedLock 是并发包里面 JDK8 版本新增的一个锁,该锁提供了三种模式的读写控制,当调用获取锁的系列函数时,会返回一个 long 型的变量,我们称之为戳记(stamp),这个戳记代表了锁的状态。其中 try 系列获取锁的函数,当获取锁失败后会返回为 0 的 stamp 值。当调用释放锁和转换锁的方法时需要传入获取锁时返回的 stamp 值。// 成员变量// 锁实例// 排它锁---写锁(writeLock)try {}finally {原创 2024-09-01 08:40:08 · 1073 阅读 · 0 评论 -
02锁机制篇(D4_知识小结)
根据线程获取锁的抢占机制,锁可以分为公平锁和非公平锁,公平锁表示线程获取锁的顺序是按照线程请求锁的时间早晚来决定的,也就是最早请求锁的线程将最早获取到锁。而非公平锁则在运行时闯入,也就是先来不一定先得。ReentrantLock 提供了公平和非公平锁的实现。公平锁:ReentrantLock pairLock = new ReentrantLock(true)非公平锁:ReentrantLock pairLock = new ReentrantLock(false)原创 2024-08-30 09:01:40 · 1714 阅读 · 0 评论 -
03ThreadLocal篇(D1_ThreadLocal)
多线程访问同一个共享变量时特别容易出现并发问题,特别是在多个线程需要对一个共享变量进行写入时。为了保证线程安全,一般使用者在访问共享变量时需要进行适当的同步,如图。同步的措施一般是加锁,这就需要使用者对锁有一定的了解,这显然加重了使用者的负担。那么有没有一种方式可以做到,当创建一个变量后,每个线程对其进行访问的时候,访问的是自己线程的变量呢?其实 ThreadLocal 就可以做这件事情,虽然 ThreadLocal 并不是为了解决这个问题而出现的。原创 2024-09-02 09:10:31 · 1420 阅读 · 0 评论 -
03ThreadLocal篇(D2_InheritableThreadLocal)
为了解决计算机系统中主内存与CPU之间运行速度差问题,会在CPU与主内存之间添加一级或者多级高速缓冲存储器(Cache)。这个 Cache 一般是被集成到 CPU 内部的,所以也叫 CPU Cache,如图所示是两级 Cache 结构。在 Cache 内部是按行存储的,其中每一行称为一个 Cache 行。Cache 行是 Cache 与主内存进行数据交换的单位,Cache 行的大小一般为 2 的幂次数字节。原创 2024-09-03 07:28:57 · 1730 阅读 · 0 评论 -
03ThreadLocal篇(D3_TransmittableThreadLocal)
在许多场景下,让计算机同时去做几件事情,不仅是因为计算机的运算能力强大了,还有一个很重要的原因是计算机的运算速度与 它的存储和通信子系统的速度差距太大,大量的时间都花费在磁盘I/O、网络通信或者数据库访问上。如果不希望处理器在大部分时间里都处于等待其他资源的空闲状态,就必须使用一些手段去把处理器 的运算能力“压榨”出来,否则就会造成很大的性能浪费,而让计算机同时处理几项任务则是最容易想 到,也被证明是非常有效的“压榨”手段。原创 2024-09-02 09:25:00 · 1484 阅读 · 0 评论 -
04伪共享篇(D1_伪共享)
高效并发是从JDK 5升级到JDK 6后一项重要的改进项,HotSpot虚拟机开发团队在这个版本上花 费了大量的资源去实现各种锁优化技术,如适应性自旋(Adaptive Spinning)、锁消除(Lock Elimination)、锁膨胀(Lock Coarsening)、轻量级锁(Lightweight Locking)、偏向锁(Biased Locking)等,这些技术都是为了在线程之间更高效地共享数据及解决竞争问题,从而提高程序的执行效率,接下来就让我们看一下锁的进阶历程吧。原创 2024-08-30 13:35:27 · 1469 阅读 · 0 评论 -
05并发三特性篇(D0_学习前言)
Fork/Join框架是Java 7提供的一个用于并行执行任务的框架,是一个把大任务分割成若干个小任务,最终汇总每个小任务结果后得到大任务结果的框架。我们再通过Fork和Join这两个单词来理解一下Fork/Join框架。Fork就是把一个大任务切分为若干子任务并行的执行,Join就是合并这些子任务的执行结果,最后得到这个大任务的结果。比如计算1+2+…+10000,可以分割成10个子任务,每个子任务分别对1000个数进行求和, 最终汇总这10个子任务的结果。原创 2024-09-13 12:34:39 · 945 阅读 · 0 评论 -
05并发三特性篇(D1_可见性)
我们已经知道使用锁的方式可以解决共享变量内存可见性问题,但是使用锁太笨重,因为它会带来线程上下文的切换开销。对于解决内存可见性问题,Java 还提供了一种弱形式的同步,也就是使用 volatile 关键字。该关键字可以确保对一个变量的更新对其他线程马上可见。当一个变量被声明为 volatile 时,线程在写入变量时不会把值缓存在寄存器或者其他地方,而是会把值刷新回主内存。当其他线程读取该共享变量时,会从主内存重新获取最新值,而不是使用当前线程的工作内存中的值。原创 2024-09-03 10:08:10 · 1714 阅读 · 0 评论 -
05并发三特性篇(D2_有序性)
java语言规范规定JVM线程内部维持顺序化语义。即只要程序的最终结果与它顺序化情况的结果相等,那么指令的执行顺序可以与代码顺序不一致,此过程叫指令的重排序。原创 2024-09-04 09:10:30 · 1107 阅读 · 0 评论 -
05并发三特性篇(D3_原子性)
在之前的文章中volatile关键字在并发中有哪些作用,我们知道了 volatile关键字可以保证可见性、有序性,但无法保证原子性的。今天我们来聊聊synchronized关键字,其可以同时保证三者,实现线程安全。在 Java 早期版本中,synchronized 属于 重量级锁,效率低下;不过在 Java 6 之后,Java 官方对从 JVM 层面对 synchronized 较大优化,所以现在的 synchronized 锁效率也优化得非常不错。目前不论是各种开源框。原创 2024-09-04 10:57:19 · 1790 阅读 · 0 评论 -
06CAS & AQS篇(D1_CAS)
CAS(Compare and Swap)是一种无锁操作,通过比较内存中的值与预期值是否相等来实现原子操作,解决并发环境下的数据竞争问题。原创 2024-09-05 20:26:56 · 1112 阅读 · 0 评论 -
06CAS & AQS篇(D2_AQS)
目录一、AQS是什么二、AQS的类图结构1. 主要成员变量1.1. exclusiveOwnerThread1.2. head,tail1.3. state2. 内部类2.1. ConditionObject2.2. Node3. 同步器的通用模板方法acquire(int arg)release(int arg)acquireShared(int arg)releaseShared(int arg)acquireInterruptibly(int原创 2024-09-05 20:29:04 · 1018 阅读 · 0 评论 -
07并发工具类篇(D1_常见实操并发工具类)
在JDK的并发包里提供了几个非常有用的并发工具类。CountDownLatch、CyclicBarrier 和 Semaphore工具类提供了一种并发流程控制的手段,Exchanger工具类则提供了在线程间交换数 据的一种手段。本章会配合一些应用场景来介绍如何使用这些工具类。CountDownLatch允许一个或多个线程等待其他线程完成操作假如有这样一个需求:我们需要解析一个Excel里多个sheet数据,此时可以考虑使用多线程,每个线程解析一。原创 2024-09-06 19:45:37 · 1206 阅读 · 0 评论 -
08并发相关类篇(D1_随机生成器)
ThreadLocalRandom使用ThreadLocal的原理,让每个线程都持有一个本地的种子变量,该种子变量只有在。UUID 的目的,是让分布式系统中的所有元素,都能有唯一的辨识资讯,随机数的生成需要一个默认的种子,这个种子其实是一个long类型的数字,你可以在创建Random对象时通过。每个Random实例里面都有一个原子性的种子变量用来记录当前的种子值,当要生成新的随机数时需要根据当。算随机数来计算新的种子时,多个线程会竞争同一个原子变量的更新操作,由于原子变量的更新是CAS操作,原创 2024-09-06 19:48:03 · 1160 阅读 · 0 评论 -
08并发相关类篇(D2_原子操作类)
JUC包提供了一系列的原子性操作类,这些类都是使用非阻塞算法CAS实现的,相比使用锁实现原子性操作这在性能上有很大提高。由于原子性操作类的原理都大致相同,这里讲解最简单的AtomicLong类的实现原理以及JDK 8中新增的LongAdder和LongAccumulator类的原理。有了这些基础,再去理解其他原子性操作类的实现就不会感到困难了。原创 2024-09-06 19:48:45 · 1000 阅读 · 0 评论 -
09并发容器篇(D1_阻塞队列(D1_BlockingQueue))
JDK中提供了一系列场景的并发安全队列。总的来说,按照实现方式的不同可分为阻塞队列和非阻塞队列。阻塞队列使用锁实现,而非阻塞队列则使用 CAS 非阻塞算法实现。阻塞队列(BlockingQueue)是一个支持两个附加操作的队列。这两个附加的操作支持阻塞的插入和移除方法。支持阻塞的插入方法:意思是当队列满时,队列会阻塞插入元素的线程,直到队列不满。支持阻塞的移除方法:意思是在队列为空时,获取元素的线程会等待队列变为非空。原创 2024-09-07 18:49:45 · 1712 阅读 · 0 评论 -
09并发容器篇(D1_阻塞队列(D2_ArrayBlockingQueue))
ArrayBlockingQueue 是一个环形数组,通过维护队首、队尾的指针,来优化(避免了数组的插入和删除,带来的数组位置移动)插入、删除,从而使时间复杂度为O(1).原创 2024-09-07 19:12:36 · 991 阅读 · 0 评论 -
09并发容器篇(D1_阻塞队列(D3_LinkedBlockingQueue))
LinkedBlockingDeque使用双端队列,通过ReentrantLock保证线程安全,实现了双端的线程安全的阻塞队列。原创 2024-09-08 09:56:31 · 983 阅读 · 0 评论 -
09并发容器篇(D1_阻塞队列(D4_LinkedBlockingQueue))
能是0或c>0,如果是c=0,那么说明之前消费线程已停止,条件对象上可能存在等待的消费线程,添加完数据后应该是c+1,那么有数据。程了,所以c>0唤醒消费线程得意义不是很大,当然如果添加线程一直添加元素,那么一直c>0,消费线程执行的换就要等待下一次调用消。这是因为消费线程一旦被唤醒是一直在消费的(前提是有数据),所以c值是一直在变化的,c值是添加完元素前队列的大小,此时c只可。唤醒其他消费线程,如果添加前c>0,那么很可能上一次调用的消费线程后,数据并没有被消费完,条件队列上也就不存在等待的消费线。原创 2024-09-08 09:57:35 · 990 阅读 · 0 评论 -
09并发容器篇(D1_阻塞队列(D5_DelayQueue))
DelayQueue是一个无界的BlockingQueue,用于放置实现了Delayed接口的对象,其中的对象只能在其到期时才能从队列中取走。这种队列是有序的,即队头对象的延迟到期时间最长。注意:不能将null元素放置到这种队列中。方法抛出异常返回值一直阻塞超时退出插入方法addofferput移除方法removepolltakePoll(time)检查方法elementpeekN/AN/A。原创 2024-09-09 09:32:08 · 1052 阅读 · 0 评论 -
09并发容器篇(D1_阻塞队列(D6_PriorityBlockingQueue))
PriorityBlockingQueue 真的是个神奇的队列,可以实现优先出队。最特别的是它只有一个锁,入队操作永远成功,而出队只有在空队列的时候才会进行线程阻塞。可以说有一定的应用场景吧,比如:有任务要执行,可以对任务加一个优先级的权重,这样队列会识别出来,对该任务优先进行出队。原创 2024-09-09 09:32:58 · 1455 阅读 · 0 评论 -
09并发容器篇(D1_阻塞队列(D7_SynchronousQueue))
前面谈到BlockingQueue的使用场景,并重点分析了ArrayBlockingQueue的实现原理,了解到ArrayBlockingQueue底层是基于数组实现的阻塞队列。但是BlockingQueue的实现类中,有一种阻塞队列比较特殊,就是SynchronousQueue(同步移交队列),队列长度为0。作用就是一个线程往队列放数据的时候,必须等待另一个线程从队列中取走数据。同样,从队列中取数据的时候,必须等待另一个线程往队列中放数据。这样特殊的队列,有什么应用场景呢?原创 2024-09-10 09:14:43 · 792 阅读 · 0 评论 -
09并发容器篇(D1_阻塞队列(D8_LinkedTransferQueue))
LinkedTransferQueue是一个由链表结构组成的无界阻塞TransferQueue队列。相对于其他阻塞队列,LinkedTransferQueue多了tryTransfer和transfer方法。可以算是 LinkedBolckingQueue 和SynchronousQueue 的合体。LinkedTransferQueue是一种无界阻塞队列,底层基于单链表实现,其内部节点分为数据结点、请求结点;基于CAS无锁算法实现。原创 2024-09-10 09:15:47 · 906 阅读 · 0 评论 -
09并发容器篇(D2_其它容器(D1_CopyOnWriteArrayList))
并发包中的并发List只有CopyOnWriteArrayList。CopyOnWriteArrayList是一个线程安全的ArrayList,对其进行的修改操作都是在底层的一个复制的数组(快照)上进行的,也就是使用了写时复制策略。CopyOnWriteArraylist的类图结构如图:在CopyOnWriteArrayList的类图中,每个CopyOnWriteArrayList对象里面有一个array数组对象用来存放具体元素,ReentrantLock独。原创 2024-09-11 12:19:33 · 1313 阅读 · 0 评论 -
09并发容器篇(D2_其它容器(D2_CopyOnWriteArraySet))
CopyOnWriteArraySet是线程安全的Set集合,相当于线程安全的HashSet。注意:HashSet的实现是通过散列表HashMap实现的,但是CopyOnWriteArraySet是通过动态数组CopyOnWriteArrayList实现的。原创 2024-09-11 12:20:02 · 643 阅读 · 0 评论 -
09并发容器篇(D2_其它容器(D3_ConcurrentSkipListMap))
一个可伸缩的并发实现,这个map实现了排序功能,默认使用的是对象自身的compareTo方法,如果提供了比较器,使用比较器的比较方法。简单来说ConcurrentSkipListMap是TreeMap的并发实现,但是为什么没有称之为ConcurrentTreeMap呢?这和其自身的实现有关。该类是SkipLists的变种实现,提供了log(n)的时间开销:Insertion, removal, update, and access等操作都是线程安全的。迭代器是弱一致性的,升序迭代器比降序的快。原创 2024-09-12 21:02:44 · 881 阅读 · 0 评论 -
09并发容器篇(D2_其它容器(D4_ConcurrentSkipListMap))
ConcurrentHashMap 是线程安全并且高效的 HashMap。本节让我们一起研究下该容器是如何在 保证线程安全的同时又能保证高效的操作。原创 2024-09-12 21:04:29 · 1342 阅读 · 0 评论 -
09并发容器篇(D2_其它容器(D5_ConcurrentSkipListSet))
ConcurrentSkipListSet,是JUC新增的一个集合工具类,顾名思义,它是一种SET类型。SET类型,在数学上称为“集合”,具有互异性、无序性的特点,也就是说SET中的任意两个元素均不相同(即不包含重复元素),且元素是无序的。JDK提供的默认SET实现——HashSet,其实就是采用“组合”的方式——内部引用了一个HashMap对象,以此实现SET的功能。原创 2024-09-12 20:56:33 · 1201 阅读 · 0 评论 -
09并发容器篇(D2_其它容器(D6_ConcurrentLinkedQueue))
在并发编程中,有时候需要使用线程安全的队列。如果要实现一个线程安全的队列有两种方式:一种是使用阻塞算法,另一种是使用非阻塞算法。使用阻塞算法的队列可以用一个锁(入队和出队用同一把锁)或两个锁(入队和出队用不同的锁)等方式来实现。非阻塞的实现方式则可以使用循环CAS的方式来实现。本节让我们一起来研究一下DougLea是如何使用非阻塞的方式来实现线程安全队列ConcurrentLinkedQueue的,相信从大师身上我们能学到不少并发编程的技巧。原创 2024-09-12 21:03:49 · 1033 阅读 · 0 评论 -
10线程池篇(D1_线程池)
在HotSpot VM的线程模型中,Java线程(java.lang.Thread)被一对一映射为本地操作系统线程。Java线程启动时会创建一个本地操作系统线程;当该Java线程终止时,这个操作系统线程也会被回收。操作系统会调度所有线程并将它们分配给可用的CPU。在上层,Java多线程程序通常把应用分解为若干个任务,然后使用用户级的调度器 (Executor框架)将这些任务映射为固定数量的线程;在底层,操作系统内核将这些线程映射到硬件处理器上。这种两级调度模型的示意图如图10-1所示。原创 2024-09-13 12:37:30 · 1329 阅读 · 0 评论