美妙的Java源码世界
文章平均质量分 96
在java的源码间,探索开发者的秘密
sermonlizhi
行百里者半九十
展开
-
Java 内存模型
我们常说Java可以一次编译到处运行,得益于它不同的虚拟机,正是由于Java虚拟机屏蔽了各种底层硬件和操作系统访问的差异,保证了Java程序在各种平台上运行都能保证效果一致而Java内存模型(Java Memory Model,JMM),就是Java虚拟机提供了的一种符合内存模型的规范。一、计算机内存模型1.1 内存结构在介绍JMM之前,我们先简单介绍一下什么是内存模型,为什么需要内存模型以及内存模型有哪些问题我们都知道,计算机在执行程序的时候,每一条指令都是在CPU中执行的,而执行指令的时候通常原创 2021-12-24 10:50:33 · 954 阅读 · 1 评论 -
Java 并发三大特性
在《Java 内存模型》中我们简单介绍了Java内存结构以及Java内存模型的定义,这边文章我们将介绍Java是如何来保证可见性、有序性和原子性的。一、可见性可见性是缓存一致性的抽象叫法。Java内存模型通过在共享变量修改后,将值同步回主存,在变量读取前从主存刷新变量这种依赖于主存的作为传递媒介的方式来实现可见性。我们通过一段代码来展示由于可见性导致的BUG:下面这段代码,线程A执行load()方法,线程B执行refresh()方法,按照我们通常的理解,线程B执行完refresh()方法后,变量fl原创 2021-12-24 20:45:07 · 768 阅读 · 0 评论 -
List 源码分析
在Java中,List接口的继承结构图大概如图所示,其中最上层的Iterable接口和Collection接口是所有Java集合类都需要实现的接口,Iterable接口提供了iterator()方法,可以返回一个Iterator对象,而Iterator也是一个接口,所有实现了Iterable接口的集合类都需要重写iterator()方法,并且在集合类内部还需要实现Iterator接口的next()等方法用于迭代。这篇文章就从源码的层面来看看这些常用的List接口的实现类一、ArrayList顾名思义原创 2021-12-26 09:00:13 · 1740 阅读 · 0 评论 -
Map 源码分析
与List、Set、Queue不同,Map是以<K,V>结构进行存储,其中Map接口是一个顶级接收,它定义了操作一些Map的基本方法,下面的继承图展示了一些常用的Map继承结构从源码的角度,来理解这些Map,分析它们之间具体的实现区别一、HashMap1.1 基本原理HashMap的存储在JDK1.7和JDK1.8有一些明显的区别,在JDK1.7中,HashMap用数组+链表的形式进行存储,而在JDK1.8及以上的版本中,HashMap采用数组+链表+红黑树的形式进行存储。Hash原创 2021-12-27 14:42:14 · 435 阅读 · 0 评论 -
Java 线程详解(上)
线程作为CPU调度的最小单位,它属于程序进程的子集,关于程序进程和线程的介绍,可以参考《详解操作系统进程》和《详解操作系统线程》两篇文章,这篇文章主要介绍Java线程的相关原理。一、线程创建从实现上来说,Java提供了三种创建线程的方式,但从原理上来看,其实只有一种方式,我们先从实现上来简单介绍一下这三种方式1.1 继承Thread类直接创建一个ThreadTest的实例,调用它的start()方法就可以创建一个线程了class ThreadTest extends Thread{ @Ov原创 2021-12-29 19:38:06 · 2938 阅读 · 0 评论 -
Java 线程详解(下)
一、线程生命周期在《详解操作系统进程》中,从操作系统层面介绍了进程(线程)的生命周期的变迁,在操作系统中,线程的状态主要包含了五种:初始化、等待状态、就绪状态、运行状态和终止状态但在Java中,定义了六种状态的,其中RUNNABLE状态对应运行状态和就绪状态,而等待状态在Java中细分为三种BLOCKED、WAITING、TIMED_WAITING关于这三种状态的描述,下面的源码中的注释已经解释的很清楚BLOCKED状态对应于对象锁,某个线程竞争对象锁失败时就处于阻塞状态WAITING状态对应于永原创 2021-12-30 19:53:01 · 932 阅读 · 0 评论 -
Java 线程池详解(上)
前面的文章详细的介绍线程相关的内容,但在平时的开发工作中,我们很少去直接创建一个线程使用,一般都是通过线程池的方式来进行调用。这边文章就来介绍一下Java中的线程池是怎么工作的,以及各种线程池之间有什么区别一、线程与线程池我们可以通过执行一段相同的代码,来看一下线程和线程池之间的区别创建多个线程:Long start = System.currentTimeMillis();final Random random = new Random();final List<Integer>原创 2021-12-31 15:38:30 · 767 阅读 · 0 评论 -
Java 线程池详解(下)
在上一篇文章《Java 线程池详解(上)》中,详解介绍了线程池的创建以及核心参数,在介绍核心参数的过程中,其实已经把线程池工作的原理简单介绍了一遍,这篇文章从线程池源码的角度来看分析线程池是如何执行任务的一、execute()方法首先拿到线程池状态控制变量ctl的值,它是一个AtomicInteger类型,所以先获取它的value值workerCountOf()方法来计算线程池中线程的数量,计算方式也很简单,因为CAPACITY低29位都是1,用ctl的值与CAPACITY的做与运算,因为CAPACI原创 2022-01-04 16:11:48 · 822 阅读 · 0 评论 -
深入理解CAS
一、什么是CAS?CAS(Compare and Swap,比较与交换),通常指的是一种原子操作:针对一个变量,首先比较它内存中的值与期望值与是否相同,如果相同,就给它赋一个新值。CAS的逻辑用伪代码实现如下:if(value == expectedValue){ value = newValue;}上面的伪代码展示了CAS操作是由比较和交换组成的复合操作,但CAS是一个不可分割的操作,即CAS对应的两步操作是一个原子性的,其原子性是由硬件层面来得以保证的,我们只需要知道了CAS的比较和原创 2022-01-05 16:42:00 · 682 阅读 · 0 评论 -
并发工具—Atomic原子操作类
在《深入理解CAS》中,介绍了可以通过CAS来实现线程安全的共享变量自增操作,但在平时的开发中,我们并不会直接去使用Unsafe类来进行CAS的操作,在多线程情况下对共享变量的操作,Java的java.util.concurrent.atomic包提供了一系列操作简单、性能高效并且能保证线程安全的类这些类按照操作的变量类型可以分为四大类:基本类型:AtomicInteger、AtomicBoolean、AtomicLong引用类型:AtomicReference、AtomicMarkableRefe原创 2022-01-05 20:12:51 · 427 阅读 · 0 评论 -
深入理解Synchronized(一)
一、简介JMM(Java Memory Model,Java内存模型)规范定义Java的共享内存模型,但共享内存模型随之带来的就是共享变量的线程安全问题,对JMM的理解可以参看《Java 内存模型》这篇文章。为了保证多线程对共享变量的互斥操作,Java提供了两大类型的方案,即阻塞式和非阻塞式的解决方案。阻塞的方式有Synchronized关键字和Lock锁,非阻塞的方式使用原子变量(CAS+自旋)。而在Java中,互斥和同步都可以通过Synchronized来实现,但它们还是有区别的:互斥是保证临界原创 2022-01-10 11:25:32 · 860 阅读 · 2 评论 -
深入理解Synchronized(二)
一、对象的内存布局在Hotspot虚拟机中,对象在内存中的布局分为三块区域:对象头(Header)、实例数据(Instance Data)和对其填充(Padding)。对象头:比如hash码,对象所属的年代,对象锁,锁状态标志,偏向锁(线程)ID,偏向时间,数组长度(数组对象才有)等实例数据:存放类的属性数据信息,包括父类的属性信息对齐填充:由于Hotspot虚拟机的自动内存管理系统要求对象起始地址必须是8字节的整数倍,换句话说就是任何对象的大小都必须是8字节的整数倍。对象头已经被精心设计成正好是8原创 2022-01-10 20:20:05 · 1056 阅读 · 1 评论 -
深入理解Synchronized(三)
在前面两篇关于Synchronized的文章中,已经简单介绍了Synchronized中管程模型、锁对象如何保存锁状态以及偏向锁和轻量级锁基本的锁状态转换,这篇文章更加深入介绍Synchronized中相关锁的底层原理、锁状态转换以及锁优化等内容一、轻量级锁底层原理1.1 加锁逻辑轻量锁的锁对象与线程锁记录之间的关系如下图所示:线程每次加锁,都会在线程的栈帧中分配一块锁记录的空间,锁记录里面包含了锁对象的Mark Word(displaced word)和指向锁对象的指针,而锁对象的对象头里面的p原创 2022-01-15 16:24:11 · 1197 阅读 · 1 评论 -
AQS&ReentrantLock源码解析
在Java中实现同步主要就两种方法Synchronized和Lock,前面的文章我们介绍了Synchronized是JVM层面的对象锁,它的实现基于MESA管程模型,而Lock是由Jdk实现的同样基于MESA管程模型的锁,其中Lock最核心的实现是ReentrantLock。一、AQS1.1 什么是AQS在《深入理解Synchronized》这篇文章中,已经详细介绍了MESA管程模型,它主要由共享变量、入口等待队列和条件队列组成,在Synchronized的实现中,只有一个条件队列,而在Reentra原创 2022-01-26 15:17:13 · 878 阅读 · 0 评论 -
Semaphore源码分析
一、Semaphore介绍Semaphore,俗称信号量,它是操作系统PV操作原语在JDK中的实现,同样,它也是基于AbstractQueuedSynchronizer来实现的。Semaphore通俗理解就是常说的共享锁,它可以定义共享资源的个数,只要共享资源还有,其他线程就可以执行,否则就会被阻塞。而关于操作系统中PV操作的原语可以参考《详解进程同步》这篇文章中对信号量的详细介绍Semaphore的功能非常强大,大小为1的信号量,它的功能就类似于互斥锁(ReentrantLock),同时只能有一个原创 2022-01-26 20:15:23 · 959 阅读 · 0 评论 -
CountDownLatch源码分析
一、CountDownLatch简介CountDownLatch(闭锁)是一个同步协助类,允许一个线程或多个线程阻塞等待,直到其他线程完成操作后,被阻塞的线程才会被唤醒,然后执行后面的业务逻辑。CountDownLatch的构造方法需要给定一个计数值(count)进行初始化。CountDownLatch简单理解就是一个倒计数器,调用它的await()会被阻塞,直到它的计数值(count)为0时,被阻塞的线程才会被唤醒,而调用它的countDown()方法会对计数器值-1,直到计数值为0后,之前被阻塞的线原创 2022-01-27 16:46:11 · 1466 阅读 · 2 评论 -
CyclicBarrier源码分析
一、简介在理解CyclicBarrier之前,最好先熟悉一下CountDownLatch的工作原理,这两个工具类都能达到线程等待的效果,但它们的侧重点有所不同,CountDownLatch只能使用一次,当它的计数器为0时会去唤醒所有同步队列中等待的线程,之后就不可用了。而CyclicBarrier的计数器可以被重置,达到重复使用的效果,所以它也就更适用于复杂的业务场景,比如如果计算发生错误时,可以通过重置计算器,让线程重复执行一次CyclicBarrier:回环栅栏(循环屏障),通过它可以实现让一组线程原创 2022-02-24 19:37:11 · 528 阅读 · 0 评论 -
ReentrantReadWriteLock源码分析
一、简介1.1 什么是读写锁在日常开发的过程中,经常存在一种场景:对于某些共享资源有读和写的操作,但是读操作要比写操作频繁(读多写少的情况)。在没有写操作的时候,对于读操作完全不需要考虑共享资源的安全问题(共享读),但有了写操作之后,为了保证读写的共享资源的数据一致性,就必须进行同步,即写写、写读、读写都是需要进行互斥的。如果我们直接使用ReentrantLock独占锁来解决读写共存的场景时,在高并发的情况下,性能就会受到很大影响,毕竟读读之间不需要进行互斥,于是就有了读写锁。在读多写少的情况下,读原创 2022-02-25 17:25:49 · 447 阅读 · 0 评论 -
BlockingQueue源码分析
一、阻塞队列简介队列常被用来解决生产——消费者问题,Java中定义了Queue接口以及通用的一些抽象方法public interface Queue<E> extends Collection<E> { // 添加一个元素,添加成功返回true,如果队列满了就抛出异常 boolean add(E e); //添加一个元素,添加成功返回true,如果队列满了返回false boolean offer(E e); // 删除并返回队首元素,原创 2022-03-02 18:56:57 · 504 阅读 · 0 评论 -
Fork/Join工作原理解析
一、如何高效利用CPU在介绍Fork/Join框架之前,思考一个问题,在多线程的情况下,如何才能高效的利用CPU,以常用的线程池为例,线程池的线程数设置多少合适?通常调整线程池中的线程数量其核心目的就是为了能够充分的利用CPU和内存资源,从而最大限度的提高程序的性能。在实际工作中,我们可能会根据任务类型的不同而采取不同的策略。按照一个任务执行过程中使用CPU的时间和任务总耗时的比例,可以将任务分为两种:CPU密集型任务和IO密集型任务1.1 任务类型1.1.1 CPU密集型任务CPU密集型任务也称原创 2022-03-05 12:40:51 · 1771 阅读 · 0 评论 -
CompletionService使用与源码分析
一、Future&FutureTask在《Java线程详解》这篇文章中,介绍了创建一个Java线程的三种方式,其中继承Thread类或实现Runnable接口都可以创建线程,但这两种方法都有一个问题就是:没有返回值,不能获取执行完的结果。因此后面在JDK1.5才新增了一个Callable接口来解决上面的问题,而Future和FutureTask就可以与Callable配合起来使用。而Callable只能在线程池中提交任务使用,且只能在submit()和invokeAnay()以及invokeAl原创 2022-03-06 18:11:09 · 8951 阅读 · 9 评论 -
CompletableFuture使用详解
一、简介1.1 概述在上一篇文章《CompletionService使用与源码分析》中,已经介绍过了Future的局限性,它没法直接对多个任务进行链式、组合等处理,需要借助并发工具类才能完成,实现逻辑比较复杂。而CompletableFuture是对Future的扩展和增强。CompletableFuture实现了Future接口,并在此基础上进行了丰富的扩展,完美弥补了Future的局限性,同时CompletableFuture实现了对任务编排的能力。借助这项能力,可以轻松地组织不同任务的运行顺序、原创 2022-03-08 16:38:08 · 116973 阅读 · 40 评论