Java并发编程
文章平均质量分 79
Java并发编程
优惠券已抵扣
余额抵扣
还需支付
¥19.90
¥99.00
购买须知?
本专栏为图文内容,最终完结不会低于15篇文章。
订阅专栏,享有专栏所有文章阅读权限。
本专栏为虚拟商品,基于网络商品和虚拟商品的性质和特征,专栏一经购买无正当理由不予退款,不支持升级,敬请谅解。
shangjg3
这个作者很懒,什么都没留下…
展开
-
Java并发编程(27)—— 队列同步器(AQS)的设计原理
因为head节点是当前线程创建的,而当前线程只知道有线程获取到了同步状态,但是却不知道是谁获取到了,所以此时当前线程在初始化head节点的时候,只能让head节点的thread属性为null。当前节点代表的线程,如果当前线程获取到了锁,那么当前线程所代表的节点一定处于同步队列的队首,且thread属性为null,至于为什么要将其设置为null,这是AQS特意设计的。独占式获取同步状态,如果线程成功获取了同步状态,则方法会返回,如果没有获取到同步状态,那么当前线程就会进入到同步队列中,并阻塞。原创 2024-09-09 16:24:36 · 252 阅读 · 0 评论 -
Java并发编程(26)—— Unsafe类的源码解读以及使用场景
堆外是相对于JVM的内存来说的,通常我们应用程序运行后,创建的对象均在JVM内存中的堆中,堆内存的管理是JVM来管理的,而堆外内存指的是计算机中的直接内存,不受JVM管理。相比C和C++的开发人员,作为一名Java开发人员是十分幸福的,因为在Java中程序员在开发时不需要关注内存的管理,对象的回收,因为JVM全部都帮助我们完成了。对于Java中并发编程,Java的源码里面存着这大量的Unsafe类的使用,主要使用的是和CAS操作相关的三个方法,所以搞清楚这三个方法,对看懂Java并发编程的源码有很大帮助。原创 2024-09-09 16:05:07 · 292 阅读 · 0 评论 -
Java并发编程(25)—— CAS的实现原理
假设原来的值为A,后来更新成了B,然后又更新成了A,这个时候去执行CAS的检查操作时,内存中的值还是A,就会误以为内存中的值没有变化,然后执行更新操作,实际上,这个时候内存中的值发生过变化。当CPU1向内存中改写共享变量时,会执行一个Lock前缀的指令,这个指令会发出一个LOCK#信号,这个信号会锁住总线,总线锁住期间,其他的CPU不能访问总线,不能访问总线,就意味着其他的CPU就不能通过总线向内存中读写数据,所以此时总线锁住期间,发出LOCK#信号的CPU能独占共享内存,这样就能保证原子操作了。原创 2024-09-06 18:39:01 · 228 阅读 · 0 评论 -
Java并发编程(1)—— Java线程的基本概念和五种状态
虽然 Thread 类提供了 `stop()` 方法,但其已经被标识为废弃,因为 `stop()` 只是暴力的停止线程, 但此时线程中的操作仍可能处于中间状态,此时暴力地停止就可能会产生非预期的结果。通常可以把一些不重要的线程设置为守护线程,比如监控其他线程状态的监控线程,当其他工作线程停止后,虚拟机就可以正常退出。在 Java 平台中,子线程的优先级默认与其父线程相同。线程中断与线程终止的区别在于:线程中断只是告诉目标线程,我希望你停止运行,即设置标志位,而线程是否真的停止则是由其自行决定。原创 2023-11-04 23:00:47 · 414 阅读 · 0 评论 -
Java并发编程(2)—— Java 多线程的三大特性
写缓冲器是处理器的私有部件,一个处理器的写缓冲器所存储的内容是不能被其他处理器所读取的,这就会导致一个更新即便已经发生并写入到写缓冲器,但是其他处理器上的线程读取到的还是旧值,从而导致可见性问题。同时为了保证单线下执行的正确性,处理器会将重排序指令的执行结果先写入到重排序缓冲器(ROB,Recorder Buffer)中,之后再按照这些指令被处理器读取的顺序提交到寄存器或者主内存中,因此虽然指令是乱序执行的,但结果却是顺序提交的,从而能够保证在单线程下的正确性。原创 2023-11-05 11:10:19 · 374 阅读 · 0 评论 -
Java并发编程(3)—— Java多线程的锁机制和无锁并行
Java 虚拟机在实现 monitorenter 字节码(申请锁)和 monitorexit 字节码(释放锁)时需要借助一个原子操作(CAS操作),这个操作是比较昂贵的,因此内部锁在每次被线程获取时,它都会将对应的线程记录为偏好线程(Biased Thread),之后此线程无论是再次申请该锁还是获取该锁,都无须借助原先昂贵的原子操作,从而减少了锁的申请与释放的开销。通常我们把被修饰的方法体和代码块称为临界区,需要注意的是必须保证多线程锁住的是同一个临界区,否则依然是线程不安全的。原创 2023-11-05 11:21:03 · 218 阅读 · 0 评论 -
Java并发编程(4)—— Java多线程的线程间的协作
为了支持多线程之间的协作,JDK 中提供了两个非常重要的方法:wait() 和 notify() ,这两个方法定义在 Object 类中,这意味着任何 Java 对象都可以调用者两个方法。当 object.notify() 方法被调用时,它就会从这个等待队列中随机唤醒一个线程。综上所述可以使用 wait() 和 notify() 配合内部锁 synchronized 可以实现线程间的等待与唤醒,如果你使用的是显示锁而不是内部锁,此时可以使用 Condition 来实现同样的效果。原创 2023-11-05 11:33:14 · 306 阅读 · 0 评论 -
Java并发编程(5)—— Java线程池
上面线程池分类中的 newSingleThreadScheduledExecutor() 和 newScheduledThreadPool() 都可以用于创建支持定时任务的线程池,它们返回的都是 ScheduledExecutorService 接口的实例。newSingleThreadExecutor(): 该方法返回一个只有一个线程的线程池。若多个任务被提交到该线程池,则多余的任务会被保存在一个任务队列中,待线程空闲,按照先入先出的顺序被执行。如果所有线程均处于工作状态,则会创建新的线程来进行处理。原创 2023-11-05 11:51:07 · 137 阅读 · 0 评论 -
Java并发编程(6)—— Java 多线程的返回对象和资源独享线程
使用 Callable 接口的限制是:其只能使用线程池提交,而不能使用单独的线程进行提交。此时通过 ExecutorService.submit() 进行提交,得到的是一个 Future 对象,它包含了线程的执行结果,当你调用其 get() 方法时,它会阻塞直至获取到线程的返回结果。ThreadLocal 是以增加资源的方式来避免竞态,它会为每一个线程创建一份私有的资源,从而避免对公共资源的竞争。1.3.4 组合多个 CompletableFuture。1. 多线程的返回对象-1.3.1 等待唤醒。原创 2023-11-05 13:45:51 · 198 阅读 · 0 评论 -
Java并发编程(7)—— 管程
假设有如下场景,对于共享变量count,初始值为0,我们需要对它进行递增或者递减操作,但是在进行操作时,需要满足如下条件,进行递增时,count的值不能大于等于10,进行递减时,count的值需要大于0。通知完成后,T1线程会从条件变量的等待队列中出来,但是此时T1不会立马执行,而是需要重新进入到管程入口处的等待队列中。如下图的管程模型图中,方框代表管程对共享变量以及操作共享变量方法的封装,在入口处有一个等待队列,当有多个线程试图进入管程时,管程只允许一个线程进入,其他的线程进入到等待队列中。原创 2023-12-18 11:24:06 · 1004 阅读 · 0 评论 -
Java并发编程(8)—— Java线程池七个参数详解
此时假设我们设置的线程数量是 CPU 核心数的 2 倍,因为计算任务非常重,会占用大量的 CPU 资源,所以这时 CPU 的每个核心工作基本都是满负荷的,而我们又设置了过多的线程,每个线程都想去利用 CPU 资源来执行自己的任务,这就会造成不必要的上下文切换,此时线程数的增多并没有让性能提升,反而由于线程数量过多会导致性能下降。通过这个公式,我们可以计算出一个合理的线程数量,如果任务的平均等待时间长,线程数就随之增加,而如果平均工作时间长,也就是对于我们上面的 CPU 密集型任务,线程数就随之减少。原创 2024-06-21 03:00:00 · 903 阅读 · 0 评论 -
Java并发编程(9)—— 7大并发容器种类原理解析与应用
与 ArrayBlockingQueue 不同的是,LinkedBlockingQueue 使用链表而非数组来存储元素,因此它可以是一个有界队列也可以是一个无界队列。它是基于跳表(Skip List)的数据结构实现的,跳表是一种可以替代平衡树的数据结构,通过多层链表实现快速的查找、插入和删除操作。它基于数组实现,可以指定队列的容量。LinkedBlockingQueue 提供了一个灵活的阻塞队列实现,既可以配置为有界队列也可以是无界队列,适用于需要高并发处理的生产者-消费者场景、任务调度和数据缓冲等应用。原创 2024-06-27 11:38:59 · 488 阅读 · 0 评论 -
Java并发编程(11)—— 线程池的4种拒绝策略
CallerRunsPolicy是Java线程池中的一种拒绝策略,当线程池中的线程数达到其最大容量,并且工作队列也满了,无法再接受新的任务时,使用CallerRunsPolicy策略会将任务交由调用者线程(即提交任务的线程)来执行。DiscardOldestPolicy是Java线程池中的一种拒绝策略,当线程池中的线程数达到其最大容量,并且工作队列也满了,无法再接受新的任务时,使用DiscardOldestPolicy策略会从队列中丢弃最旧的任务(即队列头部的任务),然后尝试再次提交当前任务。原创 2024-06-28 03:00:00 · 490 阅读 · 0 评论 -
Java并发编程(10)—— Java Executors类的9种创建线程池的方法及应用场景分析
然而,由于线程池的大小是固定的,如果任务提交的速率超过了线程池的处理能力,可能会导致任务在队列中等待较长时间。privilegedThreadFactory 是 Java 中 java.util.concurrent 包的 Executors 类的一个静态工厂方法,用于创建一个线程工厂,该工厂能够产生具有特权访问的线程。newSingleThreadExecutor 是 Java 中 java.util.concurrent 包的 Executors 类的一个静态工厂方法,用于创建一个单线程的执行器。原创 2024-06-28 04:00:00 · 292 阅读 · 0 评论 -
Java并发编程(12)——ReentrantLock可重入锁的实现原理
3.1、acquire方法内部先使用tryAcquire这个钩子方法去尝试再次获取锁,这个方法在NonfairSync这个类中其实就是使用了nonfairTryAcquire,具体实现原理是先比较当前锁的状态是否是0,如果是0,则尝试去原子抢占这个锁(设置状态为1,然后把当前线程设置成独占线程),如果当前锁的状态不是0,就去比较当前线程和占用锁的线程是不是一个线程,如果是,会去增加状态变量的值,从这里看出可重入锁之所以可重入,就是同一个线程可以反复使用它占用的锁。Lock接口,是对控制并发的工具的抽象。原创 2024-06-29 03:00:00 · 283 阅读 · 0 评论 -
Java并发编程(13)—— Java中的可重入锁、不可重入锁和自旋锁
可重入锁(也叫做递归锁): 指的是同一线程外层函数获得锁之后,内层递归函数仍然能获取该锁的代码,在同一个线程在外层方法获取锁的时候,在进入内层方法会自动获取锁,也就是说,线程可以进入任何一个它已经拥有的锁所同步着的代码块。可重入锁最大的作用就是避免死锁。可重入锁:指的是同一个线程外层函数获得锁之后,内层仍然能获取到该锁,在同一个线程在外层方法获取锁的时候,在进入内层方法或会自动获取该锁。不可重入锁,即若当前线程执行某个方法已经获取了该锁,那么在方法中尝试再次获取锁时,就会获取不到被阻塞。原创 2024-07-10 15:17:04 · 663 阅读 · 0 评论 -
Java并发编程(14)—— Java中的公平锁和非公平锁
在Synchronized中,当一个线程释放锁时,JVM会从等待队列中随机选择一个线程来获取锁,而不是按照申请锁的顺序来获取锁,因此Synchronized是一种非公平锁。在并发环境中,每一个线程在获取锁时会先查看此锁维护的等待队列,如果为空,或者当前线程是等待队列的第一个就占有锁,否者就会加入到等待队列中,以后会按照 FIFO 的规则获取锁。Synchronized是Java中内置的锁机制,它的锁模式是非公平锁,这是因为Synchronized的实现方式是基于对象监视器(monitor)的。原创 2024-07-10 15:27:00 · 1089 阅读 · 0 评论 -
Java并发编程(15)—— Java中的独占锁(写锁)和共享锁(读锁)
对 ReentrantReadWriteLock 其读锁是共享锁,其写锁是独占锁。读锁的共享锁可保证并发读是非常高效的,读写,写读,写写的过程是互斥的。独占锁:指该锁一次只能被一个线程所持有。对 ReentrantLock 和 Synchronized 而言都是独占锁。写入时正常,不会中断;读取时,可以共享锁。共享锁:指该锁可被多个线程所持有。2 示例(模拟缓存)原创 2024-07-11 03:15:00 · 278 阅读 · 0 评论 -
Java并发编程(16)—— 乐观锁、悲观锁和CAS算法实现
操作员 B 完成了操作,也将版本号( version=1 )试图向数据库提交数据( balance=$80 ),但此时比对数据库记录版本时发现,操作员 B 提交的数据版本号为 1 ,数据库记录当前版本也为 2 ,不满足 “ 提交版本必须等于当前版本才能执行更新 “ 的乐观锁策略,因此,操作员 B 的提交被驳回。悲观锁总是假设最坏的情况,认为共享资源每次被访问的时候就会出现问题(比如共享数据被修改),所以每次在获取资源操作的时候都会上锁,这样其他线程想拿到这个资源就会阻塞直到锁被上一个持有者释放。原创 2024-07-17 17:56:05 · 1088 阅读 · 1 评论 -
Java并发编程(17)—— ThreadLocal 让每个线程拥有自己的变量副本
然而,在一些场景下,我们并不希望数据被多个线程共享,而是希望每个线程都拥有自己独立的一份数据。ThreadLocal 是 Java 中的一个类,它提供了线程本地变量的功能。简单来说,就是为每一个使用该变量的线程提供了一个独立的变量副本。这样一来,每个线程都可以独立地改变自己的副本,而不会影响到其他线程。,gc执行后key引用的对象回收,导致内存泄漏。,那么gc执行后,key的引用仍然存在,仍然无法进行回收,内存泄露仍然存在。不仅简化了多线程编程中的数据管理,还能提高程序的并发性能。类的内部结构比较简单。原创 2024-08-13 03:15:00 · 367 阅读 · 0 评论 -
Java并发编程(18)—— 常用并发工具类
生产者线程将元素放入队列,消费者线程从队列中取元素,队列为空时消费者线程阻塞。让一组线程到达一个共同的同步点,然后一起继续执行。是一个线程安全的队列,支持阻塞操作,适用于生产者-消费者模式。控制访问资源的线程数,可以用来实现限流或访问控制。适用于需要所有线程在某个点都完成后再继续的场景。适用于主线程需要等待多个子线程完成任务的场景。是一个线程安全且高效的哈希表,支持并发访问。在资源有限的情况下,控制同时访问的线程数量。适用于需要频繁对数值进行无锁操作的场景。一个线程(或多个)等待其他线程完成操作。原创 2024-08-21 09:24:06 · 51 阅读 · 0 评论 -
Java并发编程(19)—— CompletableFuture异步多线程
crud系统真的很少用,一般其实都是中间件类的系统会大量的运用并发编程的知识,各种请求都是异步化的执行,比如说大家可以去看看rocketmq、elasticsearch这一类中间件的源码,他们会经常用到。或者说大家在自己公司里研发一些非crud类的系统,比如说一些公司内自研的一些底层系统,基础系统,中间件系统,其实也会经常用到并发编程,也就是异步编程的模式,那如果大家未来有可能用到异步编程的话,建议还是来了解一下今天的知识点,因为传统的异步编程其实控制你开的那个线程其实真的很麻烦。原创 2024-08-22 03:45:00 · 101 阅读 · 0 评论 -
Java并发编程(20)—— ConcurrentHashMap详解
需要高效的并发性能:相对于 Hashtable 或者 Collections.synchronizedMap() 等同步的哈希表实现,ConcurrentHashMap 在高并发情况下有更好的性能表现。1.锁的颗粒度更小,当数据落在数组上时,使用cas锁头节点,当发生冲突时数据落在节点上时,使用synchronized锁冲突的节点减少了并发冲突。ConcurrentHashMap是Java并发很重要的组件,也是大厂面试经常考察的对象,下面我就全面来详解ConcurrentHashMap。原创 2024-08-22 04:15:00 · 73 阅读 · 0 评论 -
Java并发编程(22)—— CurrentHashMap源码分析
java 并发包中提供的是一个线程安全且高效的HashMap ,也是面试的高频考点。下面将围绕主题:ConcurrentHashMap 如何实现高效地线程安全?以及在Java8中它从设计实现上有哪些演进?这篇文章一开始我以为会比较简单,但是在深入源码分析时,遇到了很大的阻碍,比前面我们分析AQS以及读写锁的源码要难理解的多,断断续续也写了4天了。如果你看完还是没有理解的话,那我在这里表示深深的歉意,同时也欢迎你和我一起沟通。原创 2024-08-30 03:00:00 · 100 阅读 · 0 评论 -
Java并发编程(21)—— Atomic 原子类总结
第一步,因为对象的属性修改类型原子类都是抽象类,所以每次使用都必须使用静态方法 newUpdater()创建一个更新器,并且需要设置想要更新的类和属性。该类将整数值与引用关联起来,可用于解决原子的更新数据和数据的版本号,可以解决使用 CAS 进行原子更新时可能出现的 ABA 问题。该类将整数值与引用关联起来,可用于解决原子的更新数据和数据的版本号,可以解决使用 CAS 进行原子更新时可能出现的 ABA 问题。基本类型原子类只能更新一个变量,如果需要原子更新多个变量,需要使用 引用类型原子类。原创 2024-08-30 03:30:00 · 93 阅读 · 0 评论 -
Java并发编程(23)—— Volatile在并发编程中的应用
它是CPU或编译器在内存随机访问操作中的一个同步点,确保此点之前的所有读写操作都执行完毕后,才能开始执行此点之后的操作。内存屏障(Memory Barrier)是一种同步机制,用于防止处理器或编译器对内存操作的指令进行重排序。在Java内存模型(JMM)中,每个线程都有自己的工作内存(也称为本地内存),用于存储共享变量的副本。是Java中的一个关键字,用于修饰变量,特别是那些会被多个线程访问和修改的共享变量。变量,JVM会在其读写过程中插入特定的内存屏障指令,以确保其内存语义的正确实现。原创 2024-08-30 03:45:00 · 83 阅读 · 0 评论 -
Java并发编程(24)—— JUC并发编程类库详解
CyclicBarrier 是一个同步辅助类,用于让一组线程到达一个屏障(barrier)时被阻塞,直到最后一个线程到达屏障时,屏障才会开门,所有被屏障拦截的线程才会继续运行。这意味着在修改操作发生时,会创建一个新的数组,并将现有的所有元素复制到新数组中,然后在新数组上进行修改。这意味着在修改操作发生时,会创建一个新的数组,然后将现有的所有元素复制到新数组中,并在新数组上进行修改。当一个线程到达交换点时,它会调用 exchange() 方法,该方法会尝试将该线程的数据与另一个线程的数据交换。原创 2024-09-03 18:53:37 · 227 阅读 · 0 评论