A collection of 49 posts

JDK并发集合13:其他队列(并发容器)

ConcurrentLinkedQueue ConcurrentLinkedQueue没有实现BlockingQueue接口,所以不是阻塞队列,不能用于线程池中,但是它是线程安全的。它由一个无界的单向链表实现。 属性 head头指针,tail尾指针。head/tail的更新可能落后于节点的入队和出队,因为它不是直接对head/tail指针进行CAS操作的,而是对Node中的item进行操作。 private transient volatile Node<E& ...

JDK并发集合11:ConcurrentMap(并发容器)

ConcurrentMap ConcurrentMap是一个接口,定义了能够支持并发访问的map集合,定义了以下几个方法(节选): getOrDefault:获取不到则返回默认值; putIfAbsent:如果没有这个key存在,则插入默认值; computeIfAbsent:传入的是一个mappingFunction,如果key不存在则通过mappingFunction设置默认值; computeIfPresent:传入的是一个mappingFunction,如果key存在则通过mappingFunction设置默认值; compute:传入一个remappingFunction,对value进行remappingFunction运行,如果结果为空则删除key, ...

JDK并发集合10:CopyOnWrite(并发容器)

CopyOnWrite的应用场景是多线程场景下多读少写,读的时候不加锁,修改数据的时候是通过拷贝一份数据进行修改,修改完后通过锁的形式写回来更新数据的。 CopyOnWriteArrayList CopyOnWriteArrayList的类继承图: 实现List提供列表的增删改查功能。 实现RandomAccess提供随机访问。 实现Cloneable可被克隆。 实现Serializable可被序列化。 锁 使用ReentrantLock用于修改的时候加锁。与ArrayList一样,内部也是通过一个Object[]数组来存储数据的。注意array加了volatile,这样在修改的时候切换数组就可以立即生效。还加了transient,自己实现序列化方法。 /** The ...

JDK并发集合9:其他(同步工具)

CyclicBarrier CyclicBarrier是一个类似于CountDownLatch功能的类,其用于“循环栅栏”,它会阻塞一些线程直到所有线程同时到达了await再继续往下执行。和CountDownLatch区别是: 不用countdown 可复用 属性 其内部实现是使用了ReentrantLock和条件锁: 未到齐,trip.await() 到齐了,trip.signalAll() /** The lock for guarding ...

JDK并发集合8:StampedLock(同步工具)

之前的ReentrantReadWriteLock读写锁比ReentrantLock的并发度高,但是读的时候还是需要加锁,即悲观锁。我们来看一种乐观锁的实现,可以进一步提高并发度。 乐观读 StampedLock具有三种模式:写模式、读模式、乐观读模式。它比ReentrantReadWriteLock多了一种“乐观读”的模式: 读时不加锁 使用的时候发现数据被修改再升级为“悲观读” 相当于降低了“读”的地位,升高了“ ...

JDK并发集合5:ReentrantLock(同步工具)

ReentrantLock 我们可以通过ReentrantLock来加深对AQS的理解。 内部API ReentrantLock内部的同步器Sync继承于AQS。 abstract static class Sync extends AbstractQueuedSynchronizer {} static final class FairSync extends Sync {} static final class NonfairSync ...

JDK并发集合4:AQS原理(同步工具)

接下来我们正式进入JDK并发集合的章节。 Treiber Stack Treiber Stack是一种可扩展的无锁栈,利用细粒度的并发原语CAS来实现的。在FutureTask中会用到。 入栈 单链表保存了各个元素 入栈时,首先创建一个newHead,把next指针指向top 通过CAS替换top为newHead,CAS的条件是top==oldHead 出栈 创建一个新的指针,指向top的next元素,假设其next元素为newHead CAS将top指向newHead, ...

JDK并发集合3:一致性问题(基础篇)

JMM就是为了解决多线程环境下共享变量的一致性问题,那么一致性包含哪些内容呢?它包括可见性、有序性和原子性三方面。 原子性 一个操作是不可中断的,要么全部执行成功要么全部执行失败。 JMM模型中八种操作是原子性的。 可见性 所有线程都能看到共享内存的最新状态。 JMM是通过在变更修改后同步回主内存,在变量读取前从主内存刷新变量值来实现的,它是依赖主内存的。而当某个线程的工作内存对这个变量进行修改时并没有及时同步到主内存中,这种情况下该变量对于其他线程来说是不可见的。 这里需要注意几个关键字: volatile:修改后立即刷回主内存,读取时从主内存读取 synchronized: ...

JDK并发集合2:JMM模型(基础篇)

上一篇讲到CPU提供了两个接口让用户可以自己刷新Store buffer和失效队列,这一篇我们来看下为了更好的理解如何实现同步的可见性,JMM抽象出来的内存屏障Memory Barrier。 JMM内存模型 java作为一个跨平台的语言,需要面对不同的底层硬件系统,它设计一个中间层模型来屏蔽底层的硬件差异,给上层的开发者一个一致的使用接口。JMM模型就是这样一个中间层的模型。 JMM内存模型定义: Java内存模型(Java Memory Model,JMM)是在硬件内存模型基础上更高层的抽象,它屏蔽了各种硬件和操作系统对内存访问的差异性,从而实现让Java程序在各种平台下都能达到一致的并发效果。 ...

JDK并发集合1:CPU缓存(基础篇)

CPU缓存 因为CPU运算速度和内存的读取速度相差了很多个数量级,所以在CPU这里通常会有几级缓存,如下图: 对于现代处理起来说,一个CPU通常有两个逻辑线程,如上图的Core0和Core1。每个Core有自己的L1 Cache,其中又分为存储指令的L1i和存储数据的L1d。同一个物理CPU的逻辑核心共享同一个L2 Cache,所有的物理CPU共享L3 Cache,这三层Cache的速度对比如下: L1:紧靠CPU内核 L2:比L1大一些,慢一些 ...

synchronized源码分析

monitorenter 获取锁是通过monitorenter,我们来看下源码实现 IRT_ENTRY_NO_ASYNC(void, InterpreterRuntime::monitorenter(JavaThread* thread, BasicObjectLock* elem)) #ifdef ASSERT thread->last_frame( ...

Object的wait/notify方法

背景 上一篇说到LockSupport的park/unpark,这一篇就来说说Object的wait/notify方法。 Object是一切类的父类,wait方法是在Object中实现的。 public final native void wait(long timeout) throws InterruptedException; wait ObjectSynchronizer Object的wait是在ObjectSynchronizer中实现的。注意: ...

Park/Unpark源码

背景 在读JUC源码的时候经常看到LockSupport.park/unpark阻塞和唤醒线程,我们来看看它们和Object的wait/signal有什么区别。 unpark LockSupport.java public static void unpark(Thread thread) { if (thread != null) UNSAFE.unpark( ...

插件化框架

因为在写一个多租户可用的框架,希望在上线的时候各个租户能够尽量做到互不影响,所以写了一个插件化加载的方法。中间踩了不少坑,写篇文章记一下。 1 获取ClassLoader 1.1 上游获取ClassLoader /** * 反射设置addUrl为可达 * @return */ private static Method initAddMethod(){ try{ Method add = URLClassLoader. ...

单例的几种实现方式

饿汉模式 public class Singleton { private static Singleton instance = new Singleton(); private Singleton(){}; public Singleton getInstance(){ return instance; } } 懒汉模式 public class ...

ThreadLocal内存泄漏问题

上一篇做了个实验,不正确的使用ThreadLocal可能会导致内存泄漏,这一篇就来讲一下为什么ThreadLocal会导致内存泄漏。 ThreadLocal实现原理 ThreadLocal的核心是一个ThreadLocalMap,ThreadLocalMap中保存了一个Entry数组,Entry继承了ThreadLocal的弱引用:ThreadLocalMap中元素的key为当前ThreadLocal对象,而value对应线程的变量副本,每个线程可能存在多个ThreadLocal。 static class Entry extends WeakReference<ThreadLocal<?>> ...

JVM排查问题

挑个趁手的好工具 mac中安装MAT,不需要先装Eclipse,可以直接安装MAT。 下载 https://www.eclipse.org/mat/downloads.php 启动 下载完成后解压,到目录mat.app/Contents/MacOS 下启动 ./MemoryAnalyzer ...

java一些参数

CPU消耗过高会引起上下文切换的增加,但并不代表这个就不正常了。正常情况下上下文切换在几百到几千,高峰时段会上升至几万,甚至几十万。 GC在核心业务应用服务中越久发生越合适,且GC的时间不要太长。一般生产环境的FGC几天一次是比较正常的。 单核CPU的线程安全问题:线程安全除了要保证可见性,还需要保证原子性、有序性。 ...

VisualVM

线程数=N(CPU核数)*(1+WT(线程等待时间)/ST(线程时间运行时间)) VisualVM查看 'self time' is a 'wall-clock' time spent in method itself (without ...

volatile内存模型

Java中的volatile CPU的读写操作 CPU在cache line状态的转化期间是阻塞的,经过长时间的优化,在寄存器和L1缓存之间添加了LoadBuffer、StoreBuffer来降低阻塞时间,LoadBuffer、StoreBuffer,合称排序缓冲(Memoryordering Buffers (MOB)),Load缓冲64长度,store缓冲36长度,Buffer与L1进行数据传输时,CPU无须等待。 CPU执行load读数据时,把读请求放到LoadBuffer,这样就不用等待其它CPU响应, ...

MAC下使用HSDIS生成汇编代码并使用JITWatch查看

jdk 安装jdk8 笔者之前使用的java12版本愣是不能使用HSdis和JITWatch,于是就安装了JDK8。 前往Oracle官网下载jdk8: https://www.oracle.com/java/technologies/javase-jdk8-downloads.html 多版本管理 试了一下使用jenv不能用,还是老实export JAVA_HOME参数。 环境变量配置 /usr/ ...

MAC编译openjdk

配置mac环境:brew install freetype ccache,安装xcode,gcc和make都需要 代码推荐https://hg.openjdk.java.net/jdk/jdk12/ 去获取zip包(点击browse->zip) 下载解压后 chmod ...

JVM字节码调用指令

invokestatic:用于调用静态方法。 invokespecial:用于调用私有实例方法、构造器,以及使用 super 关键字调用父类的实例方法或构造器,和所实现接口的默认方法。 invokevirtual:用于调用非私有实例方法。 invokeinterface:用于调用接口方法。 invokedynamic:用于调用动态方法。 ...

JVM内存 — 《java核心技术》

JVM内存区域划分 程序计数器,每个线程都有自己的程序计数器,存储当前线程正在执行的Java方法的JVM指令地址; Java虚拟机栈,每个线程在创建时都会创建一个虚拟机栈,内部保存一个个栈帧,对应着一次次方法调用。如果在该方法中调用了其他方法,则会创建新的栈帧。栈帧中存储着局部变量表、操作数栈、动态链接、方法正常退出或异常退出的定义等; 堆,放置Java对象实例,是垃圾收集器重点照顾的区域; 方法区,所有线程共享的一块内存区域,用于存储元数据, ...

Java的类加载过程 - 《Java核心技术》

Java类加载步骤 加载:将字节码数据从不同的数据源读取到JVM中,并映射为JVM认可的数据结构; 链接:把原始类定义信息平滑的转化入JVM,包括: 验证:验证字节信息是符合Java虚拟机规范的。 准备:创建类或接口中的静态变量,并初始化静态变量的初始值。 解析:将常量池中的符号引用替换为直接引用。 初始化:真正执行类初始化的代码逻辑,包括静态字段赋值、执行类定义中的静态初始化块内的逻辑。 双亲委派模型 简单来说就是当类加载器试图加载某个类型的时候, ...

LongAdder

LongAdder是jdk8新增的用于高并发环境的计数器。 Atomicxxx使用硬件级别的指令CAS来更新计数器的值,在高并发的情况下每次只能有一个线程能成功,竞争失败的线程会非常多,白白浪费了很多cpu事件,因为竞争失败的线程会自旋。 jdk8的AtomicLong: // jdk1.8的AtomicLong的实现代码,这段代码在sun.misc.Unsafe中 // 当线程竞争很激烈时,while判断条件中的CAS会连续多次返回false,这样就会造成无用的循环,循环中读取volatile变量的开销本来就是比较高的 // 因为这样,在高并发时,AtomicXXX并不是那么理想的计数方式 public ...

Java并发类库提供的线程池有哪几种? 分别有什么特点? - 《Java核心》笔记

通常都是使用Executors提供的通用线程池创建方法去创建不同配置的线程池,主要区别在于不同的ExecutorService类型或者不同的初始参数,主要分为五类: newCachedThreadPool() 用于处理大量短时间工作任务的线程池。会试图缓存线程并重用,当无缓存线程可用时,会创建新的工作线程。如果线程闲置时间超过60秒,则被终止并移出缓存。长时间闲置时,这种线程池不会消耗什么资源。其内部使用SynchronousQueue作为工作队列。 newFixedThreadpool(int nThreads) 重用固定数目的线程,背后使用的是无界的工作队列。如果任务数量超过了nThread,将在工作队列中等待空闲线程出现。 ...

AQS(转)

简介 AQS:AbstractQueuedSynchronizer,队列同步器,Java中同步组件的基础框架。JUC并发包中的核心基础组件。 AQS解决了子类实现同步器时涉及到的大量细节问题,例如获取同步状态、FIFO同步队列。 基于AQS构建的同步器中,只能在一个时刻发生阻塞,从而降低上下文切换的开销,提高了吞吐量。 AQS的主要使用方式是继承,子类通过继承同步器并实现它的抽象方法来管理同步状态。 AQS使用一个int类型的成员变量state来表示同步状态,当state>0时表示已经获取了锁,当state ...