newCachedThreadPool为什么使用SynchronousQueue

问题 Executors包装的newCachedThreadPool使用的是无容量的SynchronousQueue,为什么一定要用SynchronousQueue呢? public static ExecutorService newCachedThreadPool() { return new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS, new SynchronousQueue< ...

redis高性能概述

背景 今天来讲一下redis高性能的原因,如何以单线程来支撑10W级别的并发。 Redis的事件循环 C10K problen C10K就是单机一万并发的问题,最初服务器是来一个连接就创建一个进程/线程,这样在并发量多的情况下就会有性能问题。 问题描述 创建的进程/线程多了,数据拷贝频繁 缓存I/O需要数据拷贝,其拷贝流程是:磁盘 -> ...

redis的几个数据结构——quicklist

背景 redis的内存管理以追求极致著称,我们来看看它怎么管理容易产生内存碎片的链表。 双向链表 双向链表便于在表的两端进行push和pop操作,但是它的内存开销比较大。首先,它在每个节点上除了要保存数据之外,还要额外保存两个指针;其次,双向链表的各个节点是单独的内存块,地址不连续,节点多了容易产生内存碎片。 ziplist ziplist由于是一整块连续内存,所以存储效率很高。但是,它不利于修改操作,每次数据变动都会引发一次内存的realloc。特别是当ziplist长度很长的时候, ...

redis的几个数据结构——ziplist

背景 ziplist是列表、hash和zset元素较少时的存储格式: hash: 当哈希类型元素个数小于hash-max-ziplist-entries配置(默认512个); 所有值小于hash-max-ziplist-value配置(默认64个字节); sorted set: 元素个数小于zset-max-ziplist-entries配置,默认128; 所有值小于zset-max-ziplist-value配置,默认64。 实现 如下图所示,zlist由下面几个元素组成: zlbytes :字节长度,4个字节 ...

redis的几个数据结构——RedisObject

RedisObject是redis类型的核心,每个键值和参数都表示为RedisObject: typedef struct redisObject { // 类型 unsigned type:4; // 编码 unsigned encoding:4; // 对象最后一次被访问的时间,缓存淘汰使用 //使用LRU算法时存储的是对象访问时间,使用LFU算法时存储的是上次访问时间与访问次数 //LRU:unsigned的低24 bits的lru记录了redisObj的LRU ...

redis的几个数据结构 —— HyperLogLog

HyperLogLog 应用场景 在工作中经常会有uv、pv的统计,传统的实现方式就是用户日志上报到hbase,然后通过hive来查询,每日出报表。 如果是使用redis来实现,假设是使用ip来区分用户,假设存储一个ip为15个字节,300万的ip其开销是45MB,唯一ip可能每天出现数十万、数百万甚至数千万。 在redis中,有一种基于概率算法的计算集合的基数的数据结构,就是HyperLogLog,可以解决不要求百分百精度的UV计算。在上面例子中的300万ip,使用HyperLogLog只需要12字节即可计算。 UV ...

redis的几个数据结构 —— LFU

LFU 在有内存限制,需要淘汰冷数据的系统中,需要有算法来算出哪些数据算是冷数据。最常见的就是LRU,最近最少使用。但是LRU有一定的缺陷,如果有个冷数据忽然被访问,则会被提到列表最前,这样子可能会导致真正的热数据被清理掉。 假设有以下数据访问:横轴是时间线,纵轴是同一个时间点。 ~~~~~A~~~~~A~~~~~A~~~~A~~~~~A~~~~~A~~| ~~B~ ...

redis的几个数据结构 —— zset

zset实现和解析 zset是redis中比较经典的数据结构了,有序集合,带分值可排序,有序集合的硬性需求有这两条: 分值区间获取key 通过key直接获取分值 而redis中zset其各个操作的时间复杂度如下: 获取分值:O(1) 添加元素:O(M*log(N)), N 是有序集的基数, M 为成功添加的新成员的数量 ...

redis的几个数据结构 —— scan

背景 今年上半年看了redis的源码,做了两次分享,一直没有更新到blog上,趁着过年整理一波。 scan命令实现 禁用keys 通常来说,redis线上环境都会被运维进制使用keys命令,因为这个命令会造成redis阻塞。redis中的key都是存在hash表中的,所以keys命令需要把全部key拿出来一个一个对比。而redis执行命令的工作线程是单线程,所以其他命令就会因为keys命令而阻塞。 那我们怎么才能够在查找到目标键呢?在redis2.8.0的时候加入了scan命令,可以分批次扫描redis键。虽然在应用的时候会使得要查询到全部符合要求的key的时间变长, ...

FeignClient分流测试环境

背景 因为写了个分流,需要做UT,所以研究一下怎么在代码里面仿照假的下游服务器。 Service 声明服务端FirstFakeMessagingRestService。 @RestController @RequestMapping(path = "/messaging") public class FirstFakeMessagingRestService { @GetMapping(params = {"name" ...

Spring Cloud 分流

背景 因为项目上需要做子系统迁移,为了尽量减少生产的错误,所以需要做分流,先打5%流量到新的子系统,逐渐增大到100%。 各种调用方式的分流 项目中总共有三种调用方式:Feign、Zuul和OkHttp,三种都找到了拦截器的方法: Zuul 直接继承ZuulFilter即可。 public class UrlPathFilterExample extends ZuulFilter { private ...

MAC上Spring-framework源码构建

因为在看spring-framework的源码,没有一个顺手的环境,所以找了一个时间专门来构建spring-framework。 获取源码 git clone https://github.com/spring-projects/spring-framework git chekcout 5.2.2.RELEASE git pull 构建 ...

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

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

JDK并发集合12:BlockingQueue(并发容器)

BlockingQueue BlockingQueue是一类带阻塞功能的队列,使用的时候如果队列为空则阻塞,不需要调用方不断去轮询。 BlockingQueue是个接口,接口定义如下: public interface BlockingQueue<E> extends Queue<E> { boolean add(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并发集合7:AQS总结(同步工具)

其他使用AQS的并发类 Semaphore 内部Sync继承AQS: state为permit的数量 用处:做限流 CountDownLatch 内部Sync继承AQS: state保存次数 使用共享锁实现 调用await的线程阻塞在同步队列中 调用countDown的线程CAS减少state,减到0的时候一次过唤醒所有等待线程 AQS总结 之前学习了ReentrantLock、ReentrantReadWriteLock以及上面的Semaphore和CountDownLatch,我们来总结一下AQS。 state AQS里面使用一个int的状态变量state来保存同步状态,主要有两种形态: ...

JDK并发集合6:ReentrantReadWriteLock(同步工具)

读写锁是一种适用于多读少写的场景的锁,多个读线程可以用时对资源进行读取,但是写线程则是排他的。在多读少写的场景下,对比使用ReentrantLock和synchronized,能极大地提高并发度。 读写锁的特性: 互斥 读 写 读 否 是 写 是 是 类结构 ReentrantReadWriteLock本身实现了ReadWriterLock,需要实现“获取读锁” ...

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大一些,慢一些 ...

jmap

jmap JVM Memory Map命令用于生成heap dump文件,如果不使用这个命令,还可以使用-XX:+HeapDumpOnOutOfMemoryError参数来让虚拟机出现OOM的时候自动生成dump文件。 jmap不仅能生成dump文件,还可以查询finalize执行队列、Java堆和永久代的详细信息,如当前使用率、当前使用的是哪种收集器等。【内存分析】 [root@localhost jdk1.7.0_79] ...

jstack

一、jstack 介绍 jstack是java虚拟机自带的一种堆栈跟踪工具。jstack用于打印出给定的java进程ID或core file或远程调试服务的Java堆栈信息,如果是在64位机器上,需要指定选项"-J-d64",Windows的jstack使用方式只支持以下的这种方式: jstack [-l] pid 主要分为两个功能: a. 针对活着的进程做本地的或远程的线程dump; b. 针对core文件做线程dump。 ...

perf

安装 yum install -y perf perf性能分析: 生成火焰图(执行1-4步骤): 1、perf record -e cpu-clock -g -p pid (perf record -F 99 ...