ThreadLocal原理机制
ThreadLocal原理机制
参考Java多线程之隔离技术ThreadLocal源码详解
简介ThreadLocal存取的数据,总是与当前线程相关,也就是说,JVM为每个运行的线程绑定了私有的本地实例存取空间,从而为多线程环境常出现的并发访问问题提供了一种隔离机制。
ThreadLocal是如何做到为每一个线程维护变量的副本的呢?实现的思路很简单,在ThreadLocal类中有一个Map,用于存储每一个线程的变量的副本。
常用方法
T get()
返回此线程局部变量的当前线程副本中的值,如果这是线程第一次调用该方法,则创建并初始化此副本。
源码:
12345678910111213public T get() { Thread t = Thread.currentThread(); ThreadLocalMap map = getMap(t); if (map != null) { ThreadLocalMap.Entry e = map.getEntry(this); if (e != null) { ...
epoll浅析以及nio中的Selector
epoll浅析以及nio中的Selector
引用自epoll浅析以及nio中的Selector
epoll基本原理首先介绍下epoll的基本原理,网上有很多版本,这里选择一个个人觉得相对清晰的讲解(详情见reference):
首先我们来定义流的概念,一个流可以是文件,socket,pipe等等可以进行I/O操作的内核对象。
不管是文件,还是套接字,还是管道,我们都可以把他们看作流。
之后我们来讨论I/O的操作,通过read,我们可以从流中读入数据;通过write,我们可以往流写入数据。现在假定一个情形,我们需要从流中读数据,但是流中还没有数据,(典型的例子为,客户端要从socket读如数据,但是服务器还没有把数据传回来),这时候该怎么办?
阻塞:阻塞是个什么概念呢?比如某个时候你在等快递,但是你不知道快递什么时候过来,而且你没有别的事可以干(或者说接下来的事要等快递来了才能做);那么你可以去睡觉了,因为你知道快递把货送来时一定会给你打个电话(假定一定能叫醒你)。
非阻塞忙轮询:接着上面等快递的例子,如果用忙轮询的方法,那么你需要知道快递员的手机号,然后每分钟给 ...
常用Java类库
常用Java类库Runtime类
JVM虚拟机注册一个钩子,当虚拟机要关闭时,会执行预先注册的线程任务。
123456789101112Runtime.getRuntime().addShutdownHook(new Thread() { public void run() { try { logger.info("## stop the canal client"); clientTest.stop(); } catch (Throwable e) { logger.warn("##something goes wrong when stopping canal:\n{}", ExceptionUtils.getFullStackTrace(e)); } finally { logger.info("## ca ...
常见锁
常见锁基础概念:
并发(Concurrency):一个处理器“同时”处理多个任务
并行(Parallelism):多个处理器“同时”处理多个任务
互斥锁(Mutex)
同步块 synchronized block
对象锁 object.lock()
可重入锁
可重入锁,也叫做递归锁,指的是同一线程外层方法获得锁之后,内层递归方法仍然有获取该锁的代码,但不受影响。ReentrantLock和synchronized都是可重入锁。
在lock方法内,应验证线程是否为已经获得锁的线程。当unlock()第一次调用时,实际上不应释放锁。(采用计数进行统计)
可重入锁最大的特点是避免死锁。
12345678910111213141516171819202122public class Test implements Runnable{ public synchronized void get(){ System.out.println(Thread.currentThread().getId()); set(); ...
正则表达式
正则表达式常用正则表达式
规则
表达式
一个或多个汉字
^[\u0391-\uFFE5]+$
邮政编码
^[1-9]\d{5}$
QQ号码
^[1-9]\d{4,10}$
用户名(字母开头 + 数字/字母/下划线)
^[A-Za-z][A-Za-z1-9_-]+$
手机号码
^1([358][0-9]|4[579]|66|7[0135678]|9[89])[0-9]{8}$
URL
^((http|https)://)?([\w-]+\\.)+[\w-]+(/[\w-./?%&=]*)?$
18位身份证号
^(\d{6})(18|19|20)?(\d{2})([01]\d)([0123]\d)(\d{3})(\d|X|x)?$
邮箱
^[a-zA-Z_]{1,}[0-9]{0,}@(([a-zA-z0-9]-*){1,}\\.){1, ...
常见问题
常见问题
SimpleDateFormat不是线程安全的
使用过程中不要定义为静态全局变量。
正确使用:
123456789101112/*** 时间是否是今天*/public static boolean isToday(Long second) { if (second == null) { return false; } SimpleDateFormat sf = new SimpleDateFormat("yyyy-MM-dd"); String today = sf.format(System.currentTimeMillis()); String compare = sf.format(new Date(second * 1000L)); return StringUtils.equals(today, compare);}
或者使用ThreadLocal:
123456private static final ThreadLocal<DateF ...
深度解读Tomcat中的NIO模型
深度解读Tomcat中的NIO模型
引用自深度解读 Tomcat 中的 NIO 模型
摘要: I/O复用模型,是同步非阻塞,这里的非阻塞是指I/O读写,对应的是recvfrom操作,因为数据报文已经准备好,无需阻塞。
说它是同步,是因为,这个执行是在一个线程里面执行的。有时候,还会说它又是阻塞的,实际上是指阻塞在select上面,必须等到读就绪、写就绪等网络事件。
I/O复用模型解读Tomcat的NIO是基于I/O复用来实现的。对这点一定要清楚,不然我们的讨论就不在一个逻辑线上。下面这张图学习过I/O模型知识的一般都见过,出自《UNIX网络编程》,I/O模型一共有阻塞式I/O,非阻塞式I/O,I/O复用(select/poll/epoll),信号驱动式I/O和异步I/O。这篇文章讲的是I/O复用。
这里先来说下用户态和内核态,直白来讲,如果线程执行的是用户代码,当前线程处在用户态,如果线程执行的是内核里面的代码,当前线程处在内核态。更深层来 ...
JVM参数
JVM参数JVM参数
-Xms
堆最小值
-Xmx
堆最大值。-Xms与-Xmx的单位默认字节都是以k、m做单位的。通常着两个配置参数相等,避免每次空间不足,动态扩容带来的影响。
-Xmn
新生代大小
-Xss
每个线程池的堆栈大小。在jdk5以上的版本,每个线程堆栈大小为1m,jdk5以前的版本是每个线程池大小为256k。一般在相同物理内存下,如果减少 -Xss 值会产生更大的线程数,但不同的操作系统堆进程内线程数是有限制的,不能无限生成。
-XX:NewRatio
设置新生代与老年代比值,-XX:NewRatio=4 表示新生代与老年代所占比例为 1:4,新生代占比整个堆的五分之一。如果设置了-Xmn的情况下,该参数是不需要再设置的。
-XX:PermSize
设置持久代初始值,默认是物理内存的64分之一
-XX:MaxPermSize
设置持久代最大值,默认是物理内存的四分之一
-XX:MaxTenuringThreshold
新生代中对象存活次数,默认是15。(若对象再eden区,经历一次MinorGC后还活着,则被移动到Survivor ...
JVM自带命令
JVM自带命令
引用自jvm系列(四):jvm调优-命令篇
运用jvm自带的命令可以方便的在生产监控和打印堆栈的日志信息帮忙我们来定位问题!虽然jvm调优成熟的工具已经有很多:jconsole、大名鼎鼎的VisualVM,IBM的Memory Analyzer等等,但是在生产环境出现问题的时候,一方面工具的使用会有所限制,另一方面喜欢装X的我们,总喜欢在出现问题的时候在终端输入一些命令来解决。所有的工具几乎都是依赖于jdk的接口和底层的这些命令,研究这些命令的使用也让我们更能了解jvm构成和特性。
Sun JDK监控和故障处理命令有jps jstat jmap jhat jstack jinfo
jpsJVM Process Status Tool,显示指定系统内所有的HotSpot虚拟机进程。
命令格式
jps [option] [hostid]
option参数
-l:输出主类全名或jar路径
-q:只输出LVMID
-m:输入JVM启动时传递给main()的参数
-v:输出JVM启动时显示指定的JVM参数
其中[option]、[hostid]参数也可以不写。
...
JVM调优
JVM调优
如何优化Java GC
大型跨境电商 JVM 调优经历
Jvm知识汇总
海量连接服务端jvm参数调优杂记
内存结构
内存结构
JVM内存结构
java8 去除永久代增加元数据区Metaspace
虚拟机运行时的数据区
程序计数器(program counter register),一块较小的内存空间,可以看作当前线程所执行的字节码的行号指示器。由于Java虚拟机是采用多线程,通过线程切换获得时间片得到cpu的控制权。为了线程切换后能恢复到正确的执行位置。
虚拟机栈,调用一个方法时会创建一个栈帧,用于存储局部变量、对象引用、方法返回值,每一个方法从调用到执行完成,就对应着一个栈帧在虚拟机栈中入栈到出栈的过程。通过 -Xss 控制大小,如果线程请求的栈深度大于虚拟机所允许的深度,会抛出StackOverflowError。通过递归死锁会引发这种问题。
本地方法栈,与虚拟机栈相似,主要是针对native方法服务。
堆(heap),所有线程共享,通过new操作产生对象,存放对象实例,分为年轻代(eden、两个survivor)和年老代,通过 -Xmx 和 -Xms 控制大小,如果内存不足会抛OutOfMemoryError。通过GC释放。
方法区:主要是类信息、常量、静态变量,也叫持久代,通 ...
垃圾回收
垃圾回收
GC算法 垃圾收集器
Java GC 分析
Java应用频繁FullGC分析
GC日志
快速解读GC日志
CMS垃圾回收器详解
类加载机制
类加载机制
引用自jvm系列(一):java类的加载机制
什么是类的加载类的加载指的是将类的.class文件中的二进制数据读入到内存中,将其放在运行时数据区的方法区内,然后在堆区创建一个 java.lang.Class 对象,用来封装类在方法区内的数据结构。类的加载的最终产品是位于堆区中的 Class 对象, Class 对象封装了类在方法区内的数据结构,并且向Java程序员提供了访问方法区内的数据结构的接口。
类加载器并不需要等到某个类被“首次主动使用”时再加载它,JVM规范允许类加载器在预料某个类将要被使用时就预先加载它,如果在预先加载的过程中遇到了.class文件缺失或存在错误,类加载器必须在程序首次主动使用该类时才报告错误(LinkageError错误)如果这个类一直没有被程序主动使用,那么类加载器就不会报告错误。
加载 .class 文件的方式
从本地系统中直接加载
通过网络下载.class文件
从zip,jar等归档文件中加载.class文件
从专有数据库中提取.class文件
将Java源文件动态编译为.class文件
类的生命周期
其中类加载的过程包括了加载、验 ...
ArrayList
ArrayList
引用自图解集合 1 :ArrayList
包含元素ArrayList包含的元素:
元素
作用
transient Object[] elementData;
ArrayList是基于数组的一个实现,elementData就是底层的数组
private int size;
ArrayList里面元素的个数,这里要注意一下,size是按照调用add、remove方法的次数进行自增或者自减的,所以add一个null进入ArrayList,size也会加1
关注点
集合关注点
结论
ArrayList是否允许空
允许
ArrayList是否允许重复数据
允许
ArrayList是否有序
有序
ArrayList是否线程安全
非线程安全
关键方法添加元素12345public boolean add(E e) { ensureCapacityInternal(size + 1); // Increments modCount!! elementData[size++] = e; return tru ...
Collection接口
Collection接口类图
引用自Java集合类: Set、List、Map、Queue使用场景梳理
Collection类的父接口Iterable是迭代器接口。实现了Iterable接口的对象允许使用foreach进行遍历,所以,所有Collection集合对象都具有”foreach可遍历性”。
Collection代表一组Object的集合,这些Object被称作Collection的元素。Collection是一个接口,用以提供规范定义,不能被实例化使用。
Collection和Map的区别Collection和Map的区别在于容器中每个位置保存的元素类别。
Collection每个位置只能保存一个元素(对象);Map保存的是”键值对”,类似于一个小型数据库,可以通过”键”找到对应的”值”。
ConcurrentHashMap
ConcurrentHashMapHashMap在put的时候,插入的元素数量超过了容量(由负载因子决定)的范围是会触发扩容操作,就是rehash,这个会重新将原数组的内容重新hash到新的扩容数组中,在多线程的环境下,存在同时其他的元素也在进行put操作,如果hash值相同,可能出现同时在同一数组下用链表表示,造成闭环,导致在get时会出现死循环,所以HashMap是线程不安全的。
我们来了解另一个键值存储集合HashTable,它是线程安全的,它在所有涉及到多线程操作的都加上了synchronized关键字来锁住整个table,这就意味着所有的线程都在竞争一把锁,在多线程的环境下,它是安全的,但是无疑是效率低下的。
其实HashTable有很多的优化空间,锁住整个table这么粗暴的方法可以变相的柔和点,比如在多线程的环境下,对不同的数据集进行操作时其实根本就不需要去竞争一个锁,因为他们不同hash值,不会因为rehash造成线程不安全,所以互不影响,这就是锁分离技术,将锁的粒度降低,利用多个锁来控制多个小的table,这就是这篇文章的主角ConcurrentHashMap JDK ...
CopyOnWriteArrayList
CopyOnWriteArrayList
引用自图解集合 3 : CopyOnWriteArrayList
CopyOnWriteArrayList位于java.util.concurrent包下,可想而知,这个类是为并发而设计的
CopyOnWriteArrayList,顾名思义,Write的时候总是要Copy,也就是说对于CopyOnWriteArrayList,任何可变的操作(add、set、remove等等)都是伴随复制这个动作的
关注点
集合关注点
结论
CopyOnWriteArrayList是否允许空
允许
CopyOnWriteArrayList是否允许重复数据
允许
CopyOnWriteArrayList是否有序
有序
CopyOnWriteArrayList是否线程安全
线程安全
添加元素对于CopyOnWriteArrayList来说,增加、删除、修改、插入的原理都是一样的,所以用增加元素来分析以下Copy’O’n’Write’Array’List的底层实现机制就可以了。
12345public static void mai ...
File类
File类
引用自java中的IO整理
初始化变量
123456private static final String FILENAME = "E:\\hello.txt";private static final String FILENAME_WITH_SEPARATOR = "E:" + File.separator + "world.txt";private static final String DIRECTORY_NAME = "E:" + File.separator + "hello" + File.separator + "world";private static final String ROOT_DIRECTORY = "E:" + File.separator;private static final String SUCCESS = "成功";private static final Stri ...
HashMap
HashMap
引用自图解集合 4 :HashMap
HashMap是一种非常常见、方便和有用的集合,是一种键值对(K-V)形式的存储结构,下面将还是用图示的方式解读HashMap的实现原理。
关注点
集合关注点
结论
HashMap是否允许空
Key和Value都允许为空
HashMap是否允许重复数据
Key重复会覆盖、Value允许重复
HashMap是否有序
无序,特别说明这个无序指的是遍历HashMap的时候,得到的元素顺序基本不可能是put的顺序
HashMap是否线程安全
非线程安全
关键方法添加数据HashMap的存储单元Entry:
123456789101112/** * Basic hash bin node, used for most entries. (See below for * TreeNode subclass, and in LinkedHashMap for its Entry subclass.) */static class Node<K,V> implements Map.Entry<K,V& ...
Java泛型
Java泛型
引用自java 泛型详解-绝对是对泛型方法讲解最详细的,没有之一
概述泛型,即”参数化类型”。一提到参数,最熟悉的就是定义方法时有形参,然后调用此方法时传递实参。那么参数化类型怎么理解呢?顾名思义,就是将类型由原来的具体的类型参数化,类似于方法中的变量参数,此时类型也定义成参数形式(可以称之为类型形参),然后在使用/调用时传入具体的类型(类型实参)。
泛型的本质是为了参数化类型(在不创建新的类型的情况下,通过泛型指定的不同类型来控制形参具体限制的类型)。 也就是说在泛型使用过程中,操作的数据类型被指定为一个参数,这种参数类型可以用在类、接口和方法种,分别被称为泛型类、泛型接口、泛型方法。
示例1234567List list = new ArrayList();list.add("aaa");list.add(100);for (int i = 0; i < list.size(); i++) { String item = (String) list.get(i); System.out.println(ite ...