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(); ...
常见问题
常见问题
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 ...
正则表达式
正则表达式常用正则表达式
规则
表达式
一个或多个汉字
^[\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, ...
深度解读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复用。
这里先来说下用户态和内核态,直白来讲,如果线程执行的是用户代码,当前线程处在用户态,如果线程执行的是内核里面的代码,当前线程处在内核态。更深层来 ...
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 ...
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 ...
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 ...
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 ...
LinkedHashMap
LinkedHashMap大多数情况下,只要不涉及线程安全问题,Map基本都可以使用HashMap,不过HashMap有一个问题,就是迭代HashMap的顺序并不是HashMap放置的顺序,也就是无序。HashMap的这一缺点往往会带来困扰,因为有些场景,我们期待一个有序的Map。
LinkedHashMap解决了这个问题,它虽然增加了时间和空间上的开销,但是通过维护一个运行于所有条目的双向链表,LinkedHashMap保证了元素迭代的顺序。
关注点
集合关注点
结论
LinkedHashMap是否允许空
Key和Value都允许为空
LinkedHashMap是否允许重复数据
Key重复会覆盖、Value允许重复
LinkedHashMap是否有序
有序
LinkedHashMap是否线程安全
非线程安全
基本数据结构关于LinkedHashMap,先提两点:
LinkedHashMap可以认为是HashMap+LinkedList,即它既使用HashMap操作数据结构,又使用LinkedList维护插入元素的先后顺序
LinkedHashMap的基本实 ...
LinkedList
LinkedList
引用自图解集合 2 :LinkedList
定义LinkedList是基于链表实现的,所以先讲解一下什么是链表。链表原先是C/C++的概念,是一种线性的存储结构,意思是将要存储的数据存在一个存储单元里面,这个存储单元里面除了存放有待存储的数据以外,还存储有其下一个存储单元的地址(下一个存储单元的地址是必要的,有些存储结构还存放有其前一个存储单元的地址),每次查找数据的时候,通过某个存储单元中的下一个存储单元的地址寻找其后面的那个存储单元。
这么讲可能有点抽象,先提一句,LinkedList是一种双向链表,双向链表我认为有两点含义:
链表中任意一个存储单元都可以通过向前或者向后寻址的方式获取到其前一个存储单元和其后一个存储单元
链表的尾节点的后一个节点是链表的头结点,链表的头结点的前一个节点是链表的尾节点
包含元素LinkedList既然是一种双向链表,必然有一个存储单元,看一下LinkedList的基本存储单元,它是LinkedList中的一个内部类:
1234567891011private static class Node‹E› ...
List相关
List相关类图
List接口继承Collection接口,List集合代表一个元素有序、可重复的集合,集合中每个元素都有其对应的顺序索引。List集合允许加入重复元素,因为它可以通过索引来访问指定位置的集合元素。List集合默认按元素的添加顺序设置元素的索引。
List实现类ArrayListArrayList时基于数组实现的List类,它封装了一个可以动态扩容的数组。详情见ArrayList源码分析。
LinkedListLinkedList实现了List和Deque接口,具备列表和双端队列的一些特性。详情见LinkedList源码分析。
VectorVector和ArrayList在用法上几乎完全相同,并且Vector时线程安全的。Stack是Vector的一个子类,实现了”栈”这种数据接口,栈的特性是LIFO后进先出。
Map相关
Map相关类图
Map用于保存具有”映射关系”的数据,因此Map集合里保存着两组值,一组值用于保存Map里的key,另外一组值用于保存Map里的value。key和value都可以是任何引用类型的数据。Map的key不允许重复,即同一个Map对象的任何两个key通过equals方法比较结果总是返回false。
Map的实现类和子接口中key集的存储形式和Set集合相同(即key不能重复);Map的实现类和子接口中value集的存储形式和List非常类似(即value可以重复、根据索引进行查找)。
Map实现类HashMapHashMap保存的key-value对是无序的,判断HashMap中的两个key是否相等的标准是:两个key通过equals()方法比较返回true、同时两个key的hashCode值也必需相等。
LinkedHashMapLinkedHashMap使用双向链表来维护key-value对的次序,该链表负责维护Map的迭代顺序,与key-value对的插入顺序一致。
HashTable线程安全的Map实现类。
PropertiesProperties对象在处理属性文 ...
Queue相关
Queue相关类图
Queue用于模拟”队列”这种数据结构,其特性为FIFO先进先出。队列的头部保存着队列中存放时间最长的元素,队列的尾部保存着队列中存放时间最短的元素。新元素插入(offer)到队列的尾部,访问元素(poll)操作会返回队列头部的元素,队列不允许随机访问元素。
Queue实现类PriorityQueuePriorityQueue优先级队列,它并不是一个比较标准的队列实现,PriorityQueue保存元素的顺序并不是按照加入队列的顺序,而是按照队列元素的大小进行排序的。
DequeDeque双端队列接口,双端队列可以同时从两端来添加、删除元素,因此Deque的实现类既可以当成队列使用、也可以当成栈使用。
ArrayDequeArrayDeque是一个基于数组的双端队列,和ArrayList类似,它们的底层都采用一个可动态扩容的Object[]数组来存储集合元素。
Runnable
Runnable在Java中创建线程除了继承Thread类之外,还可以通过实现Runnable接口来实现类似的功能。实现Runnable接口必须重写其run方法。
Runnable的中文意思是”任务”,顾名思义,通过实现Runnable接口,我们 定义了一个子任务,然后将子任务交由Thread去执行。注意,这种方式必须将Runnable作为Thread类的参数,然后通过Thread类的start方法来创建一个新线程来执行该子任务。如果调用Runnable的run方法的话,是不会创建新线程的,这跟普通的方法调用没有任何区别。
事实上,查看Thread类的实现源代码会发现Thread类是实现了Runnable接口的。
在Java中,继承Thread类和实现Runnable接口这两种方式都可以用来创建线程去执行子任务,具体选择哪一种方式要看自己的需求。直接继承Thread类的话,可能比实现Runnable接口看起来更加简洁,但是由于Java只允许单继承,所以如果自定义类需要继承其他类,则只能选择实现Runnable接口。








