Java多线程学习笔记(一)—— 基础概念

****************************

*       并发编程基础概念                         *

****************************


1. cpu核心数?线程数?

基础了解:

  位宽(32位/64位CPU位宽) :  更大的cpu一次能处理更大范围的数据运算和支持更大容量的内存

   多核心(单芯片多处理器,简称CMP):其思想是将大规模并行处理器中的SMP(对称多处理器)集成到同一芯片中,各个处理器并行处理不同的线程

   多线程(简称SMT):可复制多个处理器上的结构状态,让同一处理器的多个线程同步执行并共享处理器上的资源

 核心数和线程数:增加核心数是为了增加线程数(核心数:线程数 = 1:1 由于intel引入超线程技术,核心数:线程数=1:2)

 

2. cpu时间片轮转机制?

 时间片轮转调度(又称RR调度):每个进程分配一个时间段(时间片,即该进程允许运行时间)

 

3.进程?线程?

 进程:程序运行资源分配最小单位

 线程:cpu调度的最小单位,必须依赖进程而存在

 

4.并行运行?

 程序同时所开启的运行的线程数    <= cpu数量   * cpu线程数量

 

5.并发运行?

  程序同时所开启的运行的线程数    > cpu数量   * cpu线程数量

  

6.吞吐量?

指对网络、设备、端口、虚电路或其他设施,单位时间内成功传送的数据量

网络吞吐量:在某个时刻,网络中亮哥节点之间,提供给网络应用的剩余带宽(在帧没有丢失的情况下,设备能够接收的最大速率),帮助寻找网络路径中的瓶颈

系统吞吐量:系统在单位时间内所处理的信息量(每个时间段通过处理的进程量衡量)

 

7.多并发的优势:

充分利用CPU资源

增加用户响应时间

让代码模块化、简单化(e.g:异步处理机制)


8.注意点:

线程安全性

线程之间死循环过程

线程过多会让服务器宕机


9.分布式、并发运算、并行运算


10.线程声明周期

new  --> runnable --> running --> block --> dead

 

11.守护进程?

设置方式

setDaemon(true)

解释:后台运行线程(不需要关心结束问题)

e.g:JVM垃圾回收、内存管理、连接池、监控、超时、状态等都属于守护线程


12.线程组

方便管理一些具有相同属性以及特性的线程


13.ThreadLocal线程副本

   1.通常为static 2.可添加多个key值 3.线程安全 4.remove(),用完变量以后,不然会造成内存泄露

 


14. 内存模型和多线程

线程读取数据优先级:寄存器 ---> 高速缓存 --->  内存


JLS(java统一规范)定义一个统一的内存模型JMM(规定了jvm有主内存(堆内存)和工作内存)


多线程不能通过数据传递通讯,只能通过共享变量


15. 什么是线程不安全?


线程操作一个数据结构出现修改和串行的情况,没有保证数据的一致性


16. 实现线程安全的3种方式

1.多实例(非单例)

2.java.util.concurrent下的类库

3.锁机制synchronied,lock


17.线程同步synchronized(又称隐式锁)

修饰一个方法或者代码块,保证在同一时刻最多只有一个线程执行

方法

public synchronized xxMethod()

代码块

synchronized(Object){}


18.显式锁

Lock对应API

getLock() 获取锁

lockInterruptibly() 如果当前线程未被中断,则获取锁,如果锁可用,则获取锁,并立即返回

                                                            如果锁不可用,则禁用当前线程,并且将线程处于休眠状态

tryLock() 在调用时锁为空闲状态获得锁

unLock()  释放锁

new Condition()

ReentrantLock是Lock实现类,互斥的同步器


19.ReadWriteLock 和   ReadTrantReadWriteLock

Lock的子接口,用来实现读写两个锁并存、互斥操作机制

ReadTrantReadWriteLock是ReadWriteLock的实现类


20.读写锁机制

读-读不互斥

读-写互斥

写-写互斥


21.显示锁StampedLock

悲观锁:假定会发生并发冲突,屏蔽一切违反数据完整性的操作

乐观锁:假定不会发生并发冲突,只在提交操作时检查数据完整性



22.线程安全的集合类

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机制

写时复制的容器

读写分离的思想:往容器中添加数据的时候,不是直接添加而是将容器复制,进而写入复制容器,因此不需要加锁

用于读多写少的场景

缺点:

占用内存大

数据一致性问题


Vector矢量队列

形式:通过数组保存数据

继承:AbstractList

实现:Cloneable、RandomAccess、List

如何实现线程安全:和Hashtable类似


Stringbuffer是线程安全的

Stringbuilder是非线程安全的


代码执行效率:(由高到低)

无锁、无同步安全的代码 > 方法块锁 > 类锁和方法锁


多线程之间的交互:线程阀


23.理解queue、deque、blockingQueue概念

queue<单向队列>

性质:线性表

作用:用于保存一组元素(存取元素时必须遵循先进先出(FIFO)的原则)

规范:只允许在表的前端进行删除操作(队头),表的后端进行插入操作(队尾)

队列中没有元素为空队列

deque<双端队列>

两端都可以进出的队列

当约束从队列一端进入队时,就形成了另外一种存取模式,先进后出(栈结构)

作用:双端队列主要用于栈操作,让栈操作具有可追溯性(e.g:Windows窗体路径前进、后退栈)

24. BlockingQueue(阻塞队列)

支持两个附加操作的队列(当队列为空时,获取元素的线程会等待元素变为非空;队列满时,存储元素的线程会等待队列可用)

用途:常用于生产者和消费者的场景(生产者是往队列添加元素的线程,消费者是从队列拿取元素的线程,相当于生产者的存放容器)

处理方法:

********************************************

* 处理方式    * 抛出异常  * 返回特殊值  * 一直阻塞  * 超时退出     * 

********************************************

      插入方法          add(e)  offer(e)  put(e)  offer(e,time,unit)

      移除方法         remove() poll()   take    poll(time,unit)  

      检查方法         element()  peek()  \       \

解释:

抛出异常:当队列满时,再往队列插入元素,会抛出IllegalStateException异常

                    当队列为空,获取元素,会抛出NoSuchElementException异常

返回特殊值,插入方法是否成功会返回true,移除方法从队列拿出一个元素,没有则返回null

一直阻塞:当阻塞队列满时,如果生产者往队列put元素,队列会一直阻塞生产者进程

        当阻塞队列为空时,如果消费者往队列take元素,队列会阻塞消费者进程

超时退出:如果队列满时,队列阻塞生产者一定的时间就会退出


25.ArrayBlockingQueue数组阻塞队列

概念:由数组支持有界阻塞队列

存取方式:FIFO

类型:有界缓冲区(一旦固定容量,则不要再增加)

适用:队等待的生产者线程和消费者线程进行排序的可选公平策略

缺点:公平性会降低吞吐量

优点:减少可变性和不平衡性


26.LinkedBlockingQueue链表阻塞队列

性质:链表

当生产者往队列放入一个数据时,队列会从生产者中获取数据,并缓存到队列内部,而生产者立即返回,只有当队列缓存区达到最大值缓存容量(可通过构造函数指定)时,才会阻塞队列

直到消费者从队列中消费一份数据才会生产者线程才被唤醒,反之,亦是。

之所以能高效处理并发数据是因为生产者和消费者端采用独立的锁来控制数据同步

并且意味着高并发情况下生产者和消费者可以并行的操作队列中数据


27.优先级阻塞队列PriorityBlockingQueue

支持优先级排序的无序阻塞队列(通过构造函数传下来的Compator对象决定)

不会阻塞数据生产者,而只会在没有可消费的数据阻塞消费者

注意:生产者的生产速度 < 消费者接收数据的速度(否则会耗尽堆内存空间)

内部同步控制线程的锁:公平锁


28. 延时队列DelayQueue

支持延时获取元素的使用优先级的实现无界阻塞队列

必须实现Delayed和Comparable接口(getDelay() 和 compareTo())

应用:缓存设计(保存缓存元素的有效期)和定时任务调度(保存当天将会执行的时间和任务)


29. 同步队列SynchronousQueue

不存储元素的阻塞队列

每一个put操作必须等待take操作,否则不能继续添加元素

负责把生产者数据直接传递给消费者线程,队列本身不存储任何数据

吞吐量高于其他阻塞队列

声明同步队列有两种方式:公平模式(采用FIFO)和非公平模式(采用LIFO)


30.链表双向阻塞队列LinkedTransferDueue

由链表组成的双向阻塞队列(可以从队列两端移除/插入元素)


31.链表传输队列LinkedTreansferQueue

链表构成的无界传输队列

继承BlockingQueue的一个实现类

transfer算法采用双重数据结构(之所以叫双重,是通过两个步骤完成:保留与完成)

e.g:消费者从队列中取一个元素,如果为空,则会补上空元素放入队列,消费者在这个线程上等待,这叫保留

    直到生产者意欲向队列中放入一个元素,发现最前面的元素为Null,就把当前元素填充进前面元素中,完成了传送

    

32.同步计数器CountDownLatch

在完成一组正在其他线程执行的操作之前,允许一个或多个线程一直等待。用给定的计数初始化

由于调用了countDown()方法,所以在当前计数为0之前,await会一直阻塞,之后会释放所有等待线程

使用场景:在一些应用场景,需要等到某个条件达到要求后才能做后面的操作,同时当线程完成后也会触发事件,以便进行后面操作

countDown() 倒数一次

await() 等待倒数到0

应用场景1:开5个多线程去下载,5个都执行完了,才算下载成功

应用场景2:采用多文件上传,多线程上传,当多个文件上传成功才是真的成功


33.抽象队列化同步器AbstractQueuedSynchronizer

基于FIFO队列

利用int表示状态

使用方法是继承

子类通过同步器并需要实现它的方法管理状态

提供两种机制:排他模式和共享模式

可实现自定义锁


34.同步计数器Semaphore

排队执行

35.同步计数器CyclicBarrier

同步辅助类(循环栅栏)

多个线程像多个循环,循环到点后,才往后执行

实际应用场景:统计全国业务数据(其中各省数据库独立,统计量大)

为了提高并发效率,采取多个线程计算各省数据,每个省下面用多线程,在汇总数据


35.什么是线程池?

目的:减少对象创建和销毁的时间

Java线程池实现Java高并发、Java多线程、可管理的统一调度器


Executors是线程的工厂类(工具类):方便快速的创建线程池


36.创建线程池常见的3种方法:

newSingleThreadExecutor 创建单线程的线程池

newFixedThreadPool 创建固定大小的线程池

newCachedThreadPool 创建可缓存线程池


37.线程池的优点

1.降低资源消耗(重复利用已创建线程)

2.提高响应速度

3.提高线程的可管理性

4.防止服务器过载,形成内存溢出


38.线程池工作机制

线程等待池 BlockingQueue

任务处理池PoolWorker,即正在工作的线程列表HashSet<Worker>


核心池大小(corePoolSize) 线程池的稳定峰值,达到这个值之后的线程数大小不会释放的

最大处理线程池数(maxinumPoolSize) 线程池的线程数超过corePoolSize,小于maxPoolSize,会动态创建回收线程池内的资源



39.自定义线程池和ExecutorService

自定义线程池需要用到ThreadFactory


Executor : 执行线程池的工具

ExecutorService : 线程池的真正接口(继承了Executor)

AbstractExecutorService:实现ExecutorService接口

ThreadPoolExecutor:ExecutorService默认实现 

ScheduleExecutorService:解决那些需要重复执行的问题

ScheduleThreadPoolExecutor:解决周期性调度问题的真正实现

Executors 工厂类(创建更多线程)


40. RejectedExecutionHandler

处理被丢弃的线程和异常的接口


41. ThreadPoolExecutor

线程池最核心的类

理解构造函数的参数

corePoolSize  

maxinumPoolSize

keepAliveTime 

TimeUnit

BlockingQueue

ThreadFactory

RejectedExecutionHandler


42.线程池注意问题

线程池是单例的,不能放在services方法种

线程数不能设置过大,会造成线程负载

注意死锁问题


43.JDK7的Fork/Join

思想:化繁为简、分而治之、递归的分解和合并,直到任务小到可以接收的程度

目的:并行执行任务的框架


44.Future任务机制和FutureTask

Future就是对具体的Runnable和Callable的执行结果进行取消、查询是否完成、获取结果


1.判断任务是否完成

2.是否中断任务

3.能够获取任务执行结果


FutureTask是Future的实现类

FutureTask实现RunnableFuture接口(继承Runnable和Future)


应用场景:需要统计各种类型报表记录结果(拆分成多个小线程进行模块运算,然后将结果合并作为大报表呈现结果)


45.Future相关

RecursiveAction 用于没有返回结果的任务

RecursiveTask 用于返回结果的任务


execute() 异步执行任务

invoke() 指定指定的任务,等待完成,返回结果

submit() 异步执行指定的任务,并立即返回Future对象


46.Future原理

特殊的线程池框架(分叉/结合)

ForkJoinPool利用了工作窃取类算法,使得空闲线程能够主动分担从别的线程分解的主任务


47. 计算最大并发量

根据请求大小和CPU运行机制和执行时间

(响应大小/内存大小、宽带/请求大小对应)

单台IO运行时间、CPU切换时间、程序执行时间、有没有数据库处理的等待


通过日志监控,查看用户访问哪些请求、请求哪些参数(找另一个和服务器一样的配置机器做压力测试)


48.Tomcat原理


49.Nginx

最大作用:负载均衡,请求分发


50.jvm结构

方法区  

JVM栈  :线程私有(声明周期同线程) 描述java方法执行的内存模型

堆  

本地方法栈  

程序计数器:较小的内存空间,可看作当前线程所执行的字节码的信号指示器(为确保线程切换之后恢复到正确的执行位置


评论

© dzxlovelar | Powered by LOFTER