【一】什么是pu核心数?线程数?
① 宽(32位/64位CPU位宽) : 更大的cpu一次能处理更大范围的数据运算和支持更大容量的内存
② 多核心(单芯片多处理器,简称CMP):其思想是将大规模并行处理器中的SMP(对称多处理器)集成到同一芯片中,各个处理器并行处理不同的线程
③ 多线程(简称SMT):可复制多个处理器上的结构状态,让同一处理器的多个线程同步执行并共享处理器上的资源
核心数和线程数之间的关系:增加核心数是为了增加线程数(核心数:线程数 = 1:1 由于intel引入超线程技术,核心数:线程数=1:2)
【二】cpu时间片轮转机制?
时间片轮转调度(又称RR调度):每个进程分配一个时间段(时间片,即该进程允许运行时间)
【三】进程和线程的区别
进程:程序运行资源分配最小单位
线程:cpu调度的最小单位,必须依赖进程而存在
【四】并行运行?
程序同时所开启的运行的线程数 <= cpu数量 * cpu线程数量
【五】并发运行?
程序同时所开启的运行的线程数 > cpu数量 * cpu线程数量
【六】吞吐量?
指对网络、设备、端口、虚电路或其他设施,单位时间内成功传送的数据量
① 网络吞吐量:在某个时刻,网络中亮哥节点之间,提供给网络应用的剩余带宽(在帧没有丢失的情况下,设备能够接收的最大速率),帮助寻找网络路径中的瓶颈
② 系统吞吐量:系统在单位时间内所处理的信息量(每个时间段通过处理的进程量衡量)
【七】多并发的优势
①充分利用CPU资源
②增加用户响应时间
③让代码模块化、简单化(e.g:异步处理机制)
【八】线程生命周期
new –> runnable –> running –> block –> dead
【九】守护线程?
在后台运行的线程
setDaemon(true)
e.g:JVM垃圾回收、内存管理、连接池、监控、超时、状态等都属于守护线程
【十】线程组
方便管理一些具有相同属性以及特性的线程
【十一】ThreadLocal线程副本
1.通常为static 2.可添加多个key值 3.线程安全 4.remove(),用完变量以后,请移除,不然会造成内存泄露
【十二】内存模型和多线程
①线程读取数据优先级:寄存器 —> 高速缓存 —> 内存
②JLS(java统一规范)定义一个统一的内存模型JMM(规定了jvm有主内存(堆内存)和工作内存)
③多线程不能通过数据传递通讯,只能通过共享变量
【十三】什么是线程不安全?
线程操作一个数据结构出现修改和串行的情况,没有保证数据的一致性
【十四】实现线程安全的3种方式
1.多实例(非单例)
2.java.util.concurrent下的类库
3.锁机制synchronied,lock
【十五】线程同步synchronized(又称隐式锁)
修饰一个方法或者代码块,保证在同一时刻最多只有一个线程执行
方法
public synchronized xxMethod()
代码块
synchronized(Object){}
【十六】显式锁
Lock对应API
getLock() 获取锁
lockInterruptibly() 如果当前线程未被中断,则获取锁,如果锁可用,则获取锁,并立即返回如果锁不可用,则禁用当前线程,并且将线程处于休眠状态
tryLock() 在调用时锁为空闲状态获得锁
unLock() 释放锁
new Condition()
ReentrantLock是Lock实现类,互斥的同步器
【十七】ReadWriteLock 和 ReadTrantReadWriteLock
Lock的子接口,用来实现读写两个锁并存、互斥操作机制
ReadTrantReadWriteLock是ReadWriteLock的实现类
【十八】 .读写锁机制
①读-读不互斥
②读-写互斥
③写-写互斥
【十九】显示锁StampedLock
悲观锁:假定会发生并发冲突,屏蔽一切违反数据完整性的操作
乐观锁:假定不会发生并发冲突,只在提交操作时检查数据完整性
【二十】线程安全的集合类
HashTable、ConcurrentHashMap、CopyOnWriteArrayList
CopyOnWriteArraySet、(后续会单出列出原理)
HashTable
性质:散列表
存储内容:键值对
继承:Dictionary
实现:Serializable、Map、Cloneable
影响性能因素:初始能量和加载因子
是否同步:是
是否顺序:否
如何实现线程安全:公开的方法都用synchronized修饰
ConcurrentHashMap
继承:AbstractHashMap
实现:Map、Serializable
效率:比Hashtable高
如何实现线程安全:锁分离技术(代码块锁),而非方法锁)
支持多个写入并发操作
CopyOnWriteArrayList
如何实现线程安全
1.set()、and()、remove()使用lock()加锁 unlock解锁
2.Array.copyOf() 拷贝副本
3,读操作不进行加锁、写操作进行加锁
使用情况:读操作明显大于写操作(高性能的并发读取)
CopyOnWriteArraySet
在CopyOnWriteArrayList的基础上实现Java的装饰模式
存储介质使用CopyOnWriteArrayList来存储数据
【二十一】CopyOnWrite机制
写时复制的容器
读写分离的思想:往容器中添加数据的时候,不是直接添加而是将容器复制,进而写入复制容器,因此不需要加锁
用于读多写少的场景
缺点:①占用内存大 ② 数据一致性问题
【二十一】Stringbuffer是线程安全的
Stringbuilder是非线程安全的
【二十二】代码执行效率排序
无锁、无同步安全的代码 > 方法块锁 > 类锁和方法锁
【二十三】多线程通过什么交互?
线程阀
【二十四】理解queue、deque、blockingQueue概念(后续研究)
queue<单向队列>
①性质:线性表
②作用:用于保存一组元素(存取元素时必须遵循先进先出(FIFO)的原则)
③规范:只允许在表的前端进行删除操作(队头),表的后端进行插入操作(队尾)
④扩展: 队列中没有元素为空队列
deque<双端队列>
①两端都可以进出的队列
②当约束从队列一端进入队时,就形成了另外一种存取模式,先进后出(栈结构)
③作用:双端队列主要用于栈操作,让栈操作具有可追溯性(e.g:Windows窗体路径前进、后退栈)
BlockingQueue(阻塞队列)
①支持两个附加操作的队列(当队列为空时,获取元素的线程会等待元素变为非空;队列满时,存储元素的线程会等待队列可用)
②用途:常用于生产者和消费者的场景(生产者是往队列添加元素的线程,消费者是从队列拿取元素的线程,相当于生产者的存放容器)
【二十五】ArrayBlockingQueue数组阻塞队列
概念:由数组支持有界阻塞队列
存取方式:FIFO
类型:有界缓冲区(一旦固定容量,则不要再增加)
适用:队等待的生产者线程和消费者线程进行排序的可选公平策略
缺点:公平性会降低吞吐量
优点:减少可变性和不平衡性
【二十六】LinkedBlockingQueue链表阻塞队列
性质:链表
当生产者往队列放入一个数据时,队列会从生产者中获取数据,并缓存到队列内部,而生产者立即返回,只有当队列缓存区达到最大值缓存容量(可通过构造函数指定)时,才会阻塞队列
直到消费者从队列中消费一份数据才会生产者线程才被唤醒,反之,亦是。
之所以能高效处理并发数据是因为生产者和消费者端采用独立的锁来控制数据同步
并且意味着高并发情况下生产者和消费者可以并行的操作队列中数据
【二十七】优先级阻塞队列PriorityBlockingQueue
支持优先级排序的无序阻塞队列(通过构造函数传下来的Compator对象决定)
不会阻塞数据生产者,而只会在没有可消费的数据阻塞消费者
注意:生产者的生产速度 < 消费者接收数据的速度(否则会耗尽堆内存空间)
内部同步控制线程的锁:公平锁
【二十八】延时队列DelayQueue
支持延时获取元素的使用优先级的实现无界阻塞队列
必须实现Delayed和Comparable接口(getDelay() 和 compareTo())
应用:缓存设计(保存缓存元素的有效期)和定时任务调度(保存当天将会执行的时间和任务)
【二十九】同步队列SynchronousQueue
不存储元素的阻塞队列
每一个put操作必须等待take操作,否则不能继续添加元素
负责把生产者数据直接传递给消费者线程,队列本身不存储任何数据
吞吐量高于其他阻塞队列
声明同步队列有两种方式:公平模式(采用FIFO)和非公平模式(采用LIFO)
【三十】链表双向阻塞队列LinkedTransferDueue
由链表组成的双向阻塞队列(可以从队列两端移除/插入元素)
【三十一】链表传输队列LinkedTreansferQueue
链表构成的无界传输队列
继承BlockingQueue的一个实现类
transfer算法采用双重数据结构(之所以叫双重,是通过两个步骤完成:保留与完成)
e.g:消费者从队列中取一个元素,如果为空,则会补上空元素放入队列,消费者在这个线程上等待,这叫保留
直到生产者意欲向队列中放入一个元素,发现最前面的元素为Null,就把当前元素填充进前面元素中,完成了传送
【三十二】同步计数器CountDownLatch
在完成一组正在其他线程执行的操作之前,允许一个或多个线程一直等待。用给定的计数初始化
由于调用了countDown()方法,所以在当前计数为0之前,await会一直阻塞,之后会释放所有等待线程
使用场景:在一些应用场景,需要等到某个条件达到要求后才能做后面的操作,同时当线程完成后也会触发事件,以便进行后面操作
countDown() 倒数一次
await() 等待倒数到0
应用场景1:开5个多线程去下载,5个都执行完了,才算下载成功
应用场景2:采用多文件上传,多线程上传,当多个文件上传成功才是真的成功
【三十三】抽象队列化同步器AbstractQueuedSynchronizer
基于FIFO队列
利用int表示状态
使用方法是继承
子类通过同步器并需要实现它的方法管理状态
提供两种机制:排他模式和共享模式
可实现自定义锁
【三十四】同步计数器Semaphore
排队执行
【三十五】同步计数器CyclicBarrier
同步辅助类(循环栅栏)
多个线程像多个循环,循环到点后,才往后执行
实际应用场景:统计全国业务数据(其中各省数据库独立,统计量大)
为了提高并发效率,采取多个线程计算各省数据,每个省下面用多线程,在汇总数据
【三十五】什么是线程池?
目的:减少对象创建和销毁的时间
Java线程池实现Java高并发、Java多线程、可管理的统一调度器
Executors是线程的工厂类(工具类):方便快速的创建线程池
【三十六】创建线程池常见的3种方法:
newSingleThreadExecutor 创建单线程的线程池
newFixedThreadPool 创建固定大小的线程池
newCachedThreadPool 创建可缓存线程池
【三十七】线程池的优点
1.降低资源消耗(重复利用已创建线程)
2.提高响应速度
3.提高线程的可管理性
4.防止服务器过载,形成内存溢出
【三十八】线程池工作机制
线程等待池 BlockingQueue
任务处理池PoolWorker,即正在工作的线程列表HashSet
核心池大小(corePoolSize) 线程池的稳定峰值,达到这个值之后的线程数大小不会释放的
最大处理线程池数(maxinumPoolSize) 线程池的线程数超过corePoolSize,小于maxPoolSize,会动态创建回收线程池内的资源
【三十九】自定义线程池和ExecutorService
自定义线程池需要用到ThreadFactory
Executor : 执行线程池的工具
ExecutorService : 线程池的真正接口(继承了Executor)
AbstractExecutorService:实现ExecutorService接口
ThreadPoolExecutor:ExecutorService默认实现
ScheduleExecutorService:解决那些需要重复执行的问题
ScheduleThreadPoolExecutor:解决周期性调度问题的真正实现
Executors 工厂类(创建更多线程)
【四十】RejectedExecutionHandler
处理被丢弃的线程和异常的接口
【四十一】 ThreadPoolExecutor
线程池最核心的类
理解构造函数的参数
corePoolSize
maxinumPoolSize
keepAliveTime
TimeUnit
BlockingQueue
ThreadFactory
RejectedExecutionHandler
【四十二】线程池注意问题
线程池是单例的,不能放在services方法种
线程数不能设置过大,会造成线程负载
注意死锁问题
【四十三】JDK7的Fork/Join
思想:化繁为简、分而治之、递归的分解和合并,直到任务小到可以接收的程度
目的:并行执行任务的框架
【四十四】Future任务机制和FutureTask
Future就是对具体的Runnable和Callable的执行结果进行取消、查询是否完成、获取结果
1.判断任务是否完成
2.是否中断任务
3.能够获取任务执行结果
FutureTask是Future的实现类
FutureTask实现RunnableFuture接口(继承Runnable和Future)
应用场景:需要统计各种类型报表记录结果(拆分成多个小线程进行模块运算,然后将结果合并作为大报表呈现结果)
【四十五】Future相关
RecursiveAction 用于没有返回结果的任务
RecursiveTask 用于返回结果的任务
execute() 异步执行任务
invoke() 指定指定的任务,等待完成,返回结果
submit() 异步执行指定的任务,并立即返回Future对象
【四十六】Future原理
特殊的线程池框架(分叉/结合)
ForkJoinPool利用了工作窃取类算法,使得空闲线程能够主动分担从别的线程分解的主任务
【四十七】计算最大并发量
根据请求大小和CPU运行机制和执行时间
(响应大小/内存大小、宽带/请求大小对应)
单台IO运行时间、CPU切换时间、程序执行时间、有没有数据库处理的等待
通过日志监控,查看用户访问哪些请求、请求哪些参数(找另一个和服务器一样的配置机器做压力测试)