IO模型
什么是IO?从计算机结构的视角来看的话, I/O 描述了计算机系统与外部设备之间通信的过程。 我们想要进行 IO 操作,一定是要依赖内核空间的能力。并且,用户空间的程序不能直接访问内核空间。 当想要执行 IO 操作时,由于没有执行这些操作的权限,只能发起系统调用请求操作系统帮忙完成。 因此,用户进程想要执行 IO 操作的话,必须通过 系统调用 来间接访问内核空间 我们在平常开发过程中接触最多的就是 磁盘 IO(读写文件) 和 网络 IO(网络请求和响应)。 从应用程序的视角来看的话,我们的应用程序对操作系统的内核发起 IO 调用(系统调用),操作系统负责的内核执行具体的 IO 操作。也就是说,我们的应用程序实际上本质上都是在调用OS内核提供的函数:read()、 write(),具体 IO 的执行是由操作系统的内核来完成的。 当应用程序发起 I/O 调用后,会经历两个步骤: 内核等待 I/O 设备准备好数据 内核将数据从内核空间拷贝到用户空间 Java IO模型推荐阅读:彻底理解 IO 多路复用实现机制 - 掘金...
ConcurrentHashMap全解
ConcurrentHashMap的结构JDK8之前: 一个 ConcurrentHashMap 里包含一个 Segment 数组,一个 Segment 里包含一个 HashEntry 数组,每个 HashEntry 是一个链表结构的元素 首先将数据分为一段一段(这个“段”就是 Segment)的存储,然后给每一段数据配一把锁,当一个线程占用锁访问其中一个段数据时,其他段的数据也能被其他线程访问。 ConcurrentHashMap 是由 Segment 数组结构和 HashEntry 数组结构组成。HashEntry 用于存储键值对数据。 一个 ConcurrentHashMap 里包含一个 Segment 数组,Segment 的个数一旦初始化就不能改变。 Segment 数组的大小默认是 16,也就是说默认可以同时支持 16 个线程并发写。 Segment 的结构和 HashMap 类似,是一种数组和链表结构,一个 Segment 包含一个 HashEntry 数组,每个 HashEntry 是一个链表结构的元素,每个 Segment 守护着一个 HashEntry...
HashMap全解
HashMap的结构在 JDK 1.7 版本之前, HashMap 数据结构是数组和链表,HashMap通过哈希算法将元素的键(Key)映射到数组中的槽位(Bucket)。如果多个键映射到同一个槽位,它们会以链表的形式存储在同一个槽位上,因为链表的查询时间是O(n),所以冲突很严重,一个索引上的链表非常长,效率就很低了。 所以在 JDK 1.8 版本的时候做了优化,当一个链表的长度超过8的时候就转换数据结构,不再使用链表存储,而是使用红黑树,查找时使用红黑树,时间复杂度O(log n),可以提高查询性能,但是在数量较少时,即数量小于6时,会将红黑树转换回链表。 HashMap底层是链表,但是当链表的长度大于8时,并且数组的长度大于64,此时会将链表转化为红黑树来提高查询效率。(将链表转换成红黑树前会判断,如果当前数组的长度小于...
Java内存区域
运行时数据区域Java 虚拟机在执行 Java 程序的过程中会把它管理的内存划分成若干个不同的数据区域。 JDK 1.8 和之前的版本略有不同,我们这里以 JDK 1.7 和 JDK 1.8 这两个版本为例介绍。 JDK 1.7: JDK 1.8: 线程私有的: 程序计数器 虚拟机栈 本地方法栈 线程共享的: 堆 方法区 直接内存 (非运行时数据区的一部分) Java 虚拟机规范对于运行时数据区域的规定是相当宽松的。以堆为例:堆可以是连续空间,也可以不连续。堆的大小可以固定,也可以在运行时按需扩展...
打破双亲委派机制的方式
打破双亲委派模型方法 1.自定义类加载器为了避免双亲委托机制,我们可以自己定义一个类加载器,继承ClassLoader类,然后重写 loadClass() 即可 自定义加载器的话,需要继承 ClassLoader 。如果我们不想打破双亲委派模型,就重写 ClassLoader 类中的 findClass() 方法即可,无法被父类加载器加载的类最终会通过这个方法被加载。但是,如果想打破双亲委派模型则需要重写 loadClass() 方法 为什么是重写 loadClass() 方法打破双亲委派模型呢?双亲委派模型的执行流程已经解释了: 类加载器在进行类加载的时候,它首先不会自己去尝试加载这个类,而是把这个请求委派给父类加载器去完成(调用父加载器 loadClass()方法来加载类) 重写...