volatile概念:java
volatile关键字的主要做用是使变量在多个线程间可见,不具有原子性。
多个线程的可见性:volatile。使用netty。
原子性:AtomicLong等,注意atomic类只自己方法原子性,并不能保证屡次操做的原子。数组
线程之间的通讯:缓存
线程是操做系统中独立的个体,但这些个体若是不通过特殊的处理就不能成为一个总体,线程间的通讯就成为总体的比用方式之一。
使用wait/notify方法实现线程间的通讯,注意这两个方法都是object的类的方法,换句话说Java为全部的对象都提供了这两个方法。
1.wait和notify必须配合synchronized关键字使用
2.wait方法释放锁,notify方法不能释放锁。安全
final Object lock = new Object(); synchronized(lock){ lock.notify(); } synchronized(lock){ lock.wait(); }
wait()线程必须先启动。
注意有个问题,不能实时的通知。
解决方法:final CountDownLatch countDownLatch = new CountDownLatch();
去掉synchronized,notify()改成:countDownLatch.countDown();
wait改成await();
使用wait/notify模拟网络
Queue
BlockingQueue:它是一个队列,而且支持阻塞的机制,阻塞的放入和获得数据。实现put和take
ThreadLocal
单列 static inner class 最好。多线程
public Class innerStringletion(){ private static class Singletion{ private static Singletion single = new Singletion(); } public static Singletion getInstance(){ return Singletion.single(); } }
同步类容器
同步类容器都是线程安全的,但在某些场景下可能须要加锁来保护复合操做。复合类操做:迭代、跳转、以及条件运算。
古老的Vector、HashTable,这些容器的同步功能其实都是jdk的Collections.synchronized***等工厂方法去建立实现的。
其底层的机制无非就是用传统的synchronized关键字对每一个公用的方法都进行同步,使得每次只能有一个线程访问容器的状态。不符合如今高并发的需求。
并发类容器
目的代替同步类容器
ConcurrentHashMap
CopyOnWriteArrayList
Queue,ConcurrentLinkedQueue
ConcurrentMap接口重要实现
ConcurrentHashMap
ConcurrentSkipListMap(支持并发排序功能)
ConcurrentHashMap:并发
内部使用段(Segment)来表示这些不一样的部分,每一个段其实就是一个小的HashTable,他们有本身的锁。
只要多个修改操做发生在不一样的段上,它们就能够并发进行。把一个总体分红了16个段,也就是最高支持16个线程的并发修改操做。这也是
在多线程场景时减少锁的粒度从而下降锁竞争的一种方案。而且代码中大多共享变量使用volatile关键字声明。目的是第一时间获取修改的内容。
CopyOnWrite:框架
容器即写时复制容器。通俗的理解是当咱们往一个容器添加元素的时候,不直接往当前容器添加,而是先将当前容器进行copy,复制出
一个新的容器,而后新的容器里添加元素,添加以后,再将原容器的引用指向新的容器。这样作的好处是咱们能够对copyonWrite容器进行并发的读,
不须要加锁,由于当前容器不会添加任何元素,因此copyonwrite容器也是一种读写分离的思想,读和写不一样的容器。
使用场景:读多写少。写的话,每次都须要拷贝新的容器消耗资源大。高并发
并发Queue性能
在并发队列上提供了两套实现,一个是以ConcurrentLinkedQueue为表明的高性能队列,一个是以BlockingQueue接口为表明的阻塞队列。
ConcurrentLinkedQueue:是一个适用高并发场景下的队列,经过无锁的方式,实现了高并发状态下的高性能,一般ConcurrentLinkedQueue性能好于
BlockingQueue:它是一个基于链表节点的无界线程安全队列。该队列的元素遵循先进先出的原则。头是最早加入的,尾是最近加入的,该队列不容许null元素。
add()和offer()加入元素。
poll和peek都是取头元素节点,区别在于前者会删除元素,后者不会。
ArrayBlockingQueue:基于数组的阻塞队列实现,在ArrayBlockingQueue内部,维护了一个定长的数组,以便缓存队列中的数据对象,其内部没实现读写分离
也就意味着生产和消费不能彻底并行,长度是须要定义的,能够指定先进先出或者先进后出,也叫有界队列,在不少场合很是适用。
LinkedBlockingQueue:跟上面的相反。无界队列,生产和消费并行。可是都是阻塞的。
synchronousQueue:一种没有缓冲的队列。生产者产生的数据直接会被消费者获取并消费掉。注意:先有take,才能add方法。
DelayQueue:带有延迟时间的Queue,其中的元素只有当其指定的延迟时间到了,才可以从队列中获取到该元素。DelayQueue中的元素必须实现Delayed接口,DelayQueue是
一个没有大小限制的队列,应用场景不少,好比对缓存超时的数据进行移除,任务超时处理,空闲链接的关闭等。
优先级队列:在take的时候进行排序。
Future模式
有点相似于商品订单。只要下完单,在家等着,中间的过程不用管。
客户端Future FutureDate包装类 RealData真实数据类
data对象------------->call----------------->other call
<------------call return
实际使用时,获取数据 <-------------------call return
jdk有实现。
Master-Worker模式
Matser-worker模式是经常使用的并行计算模式。它的核心思想是系统有两类进程协做工做:Master-worker进程。master负责接收的分配任务,
worker负责处理子任务。当各个worker子进程处理完成以后,会将结果返回给master,由master作概括和总结。其好处是能将一个大任务分解成若干个小任务
并行执行,从而提升系统的吞吐量。
生产者-消费者模式
在生产-消费者模式中:一般由两类线程,即若干个生产者的线程和若干个消费者的线程。生产者线程负责提交用户请求,消费者线程则负责具体处理生产者提交 的任务,在生产者和消费者之间经过共享内存缓存区进行通讯。
Executor框架
比较重要的类,Executors。
建立线程池的方法:
newFixedThreadPool()方法:该方法返回一个固定数量的线程池,该方法的线程数始终不变,当有一个任务提交时,若线程池中空闲,则当即执行,若没有,则会被
暂缓在一个任务队列中等待有空闲的线程去执行。
newSingleThreadExecutor()方法,建立一个线程的线程池,若空闲则执行,若没有空闲线程则暂缓在任务队列中。
newCachedThreadPool()方法,返回一个可根据实际状况调整线程个数的线程池,不限制最大线程数量,若用空闲的线程则执行任务,若无任务则不建立线程。而且
每个空闲线程会在60秒后自动回收。
newScheduledThreadPool()方法,该方法返回一个SchededExecutorService对象,但该线程池能够指定线程的数量。
自定义线程池使用详细
在使用有界队列时,如有新的任务须要执行,若是线程池实际线程数小于corePoolSize,则优先建立线程,若大于corePoolSize,则会将任务加入队列,若队列已满,则在总线程数不大于maximumPoolSize的
前提下,建立新的线程,若线程数大于maximumPoolSize,则执行拒绝策略。或其余自定义方式。
无界的任务队列时:LinkedBlockingQueue。与有界队列相比,除非系统耗尽资源,不然无界的队列不存在任务入队失败的状况。当有新任务到来,系统的线程数小于corePoolSize时,则新建线程执行任务。当达到
corePoolSize后,就不会继续增长。若后续仍有新的任务增长,而没有空闲的线程资源,则任务直接进入队列等待。若任务建立个处理的速度差别很大,无界队列会保持快速增加,直到内存系统耗尽。
JDK拒绝策略:
若是须要自定义拒绝策略能够实现RejectedExecutionHandler接口。
线程的大小:corePoolSize+队列的数量,有界队列。
高并发:网络端、服务(分流)、Java(限流)
Concurrent.util类
CyclicBarrier的使用:
假设有只有的一个场景:每一个线程表明一个跑步运动员,当运动员都准备好后,才一块儿出发,只有一我的没有准备好,你们都等待。
CountDownLatch使用:
它常常用于监听某些初始化操做,等待初始化执行完毕后,通知主线程继续工做。
Callable和Future使用:
就是以前的future模式。很是适合在处理很耗时很长的业务逻辑时进行使用,能够有效的减小系统的响应时间,提升系统的吞吐量。
Semaphone信号量(限流)
PV:网站的总访问量,页面浏览量或点击量,用户每刷新一次就会被记录一次。
UV:访问网站的一台电脑客户端为一个访客。在0点到24小时以内根据IP为一次。
QPS:每秒查询数,qps很大程度上表明了系统业务上的繁忙程度。每次请求的背后,可能对应着屡次磁盘I/O,屡次网络请求,多个CPU时间片等。咱们经过qps能够很是直观的
了解当前系统业务状况,一qps超过锁设定的预警阈值。能够考虑增长机器对集群扩容。
RT:请求的响应时间。
容量评估:进行多伦压力测试以后,能够对系统进行峰值评估,采用所谓的60/20原则,即80%的访问请求将在20%的时间内达到。
峰值qps=(总pv * 80%)/(60 * 60 * 24 * 20%)
机器所需数量:总的峰值qps/压测的出的单机极限qps
拿到信号量的线程可进入,不然等待。经过acquire和release获取和释放访问许可。
锁
重入锁和读写锁 ReentrantLock(重入锁)。在须要进行同步的代码加上锁定,但不要忘记最后必定要释放锁定。 同时可使用一个新的等待/通知的类,它就是Condition。这个Condition必定是针对具体某一把锁。也就是在只有锁的基础之上才会产生Condition。 Lock lock = new ReentrantLock(); Condition condition = lock.newCondition(); Condition condition1 = lock.newCondition(); condition.await()至关于await(); condition.single();至关于通知notify(). ReentrantReadWriteLock(读写锁) 其核心就是实现读写分离的锁,在高并发访问下,尤为是读多写少的状况下,性能远高于重入锁 咱们知道,同一时间内,只能有一个线程进行访问被锁定的代码,那么读写锁则不一样,其本质是分红两个锁,即读锁、写锁、在读锁下多个线程能够并发的进行访问,可是在写锁的时候,只能一个个的顺序访问。 生产者应该放在容器里。