多线程
2022-03-16 23:31:35 0 举报
AI智能生成
多线程完整介绍
作者其他创作
大纲/内容
并发工具
CountDownLatch
CountDownLatch是一个同步工具类,用来协调多个线程之间的同步,或者说起到线程之间的通信(而不是用作互斥的作用)。
Semaphore
信号量, 可以用来控制同时访问特定资源的线程数量,通过协调各个线程,以保证合理的使用资源。
CyclicBarrier
中文意思是“循环栅栏”,可以用于多线程计算数据,最后合并计算结果的场景。
CountDownLatch与CyclicBarrier比较
CountDownLatch的计数器只能使用一次。而CyclicBarrier的计数器可以使用reset()
方法重置。所以CyclicBarrier能处理更为复杂的业务场景,比如如果计算发生错误,可以重置计数器,并让线程们重新执行一次。
方法重置。所以CyclicBarrier能处理更为复杂的业务场景,比如如果计算发生错误,可以重置计数器,并让线程们重新执行一次。
CyclicBarrier还提供其他有用的方法,比如getNumberWaiting方法可以获得CyclicBarrier阻塞的线程数量。isBroken方法用来知道阻塞的线程是否被中断。比如以下代码执行完之后会返回true。
CountDownLatch会阻塞主线程,CyclicBarrier不会阻塞主线程,只会阻塞子线程。
某线程中断CyclicBarrier会抛出异常,避免了所有线程无限等待。
JAVA锁
乐观锁/悲观锁
乐观锁
总是认为不存在并发问题,每次去取数据的时候,总认为不会有其他线程对数据进行修改,因此不会上锁。但是在更新时会判断其他线程在这之前有没有对数据进行修改,一般会使用“数据版本机制”或“CAS操作”来实现。
悲观锁
认为对于同一个数据的并发操作,一定会发生修改的,哪怕没有修改,也会认为修改。因此对于同一份数据的并发操作,悲观锁采取加锁的形式。悲观的认为,不加锁并发操作一定会出问题。
独享锁/共享锁
独享锁
该锁一次只能被一个线程所持有。
共享锁
该锁可被多个线程所持有。
互斥锁/读写锁
互斥锁
在Java中的具体实现就是ReentrantLock。
读写锁
Java中的具体实现就是ReadWriteLock。
可重入锁
又名递归锁,是指在同一个线程在外层方法获取锁的时候,在进入内层方法会自动获取锁。
公平锁/非公平锁
公平锁
是指多个线程按照申请锁的顺序来获取锁。
非公平锁
是指多个线程获取锁的顺序并不是按照申请锁的顺序,有可能后申请的线程比先申请的线程优先获取锁。有可能,会造成优先级反转或者饥饿现象。
分段锁
分段锁其实是一种锁的设计,并不是具体的一种锁,对于ConcurrentHashMap而言,其并发的实现就是通过分段锁的形式来实现高效的并发操作。
偏向锁/轻量级锁/重量级锁
偏向锁
是指一段同步代码一直被一个线程所访问,那么该线程会自动获取锁。降低获取锁的代价。
轻量级锁
是指当锁是偏向锁的时候,被另一个线程所访问,偏向锁就会升级为轻量级锁,其他线程会通过自旋的形式尝试获取锁,不会阻塞,提高性能。
重量级锁
是指当锁为轻量级锁的时候,另一个线程虽然是自旋,但自旋不会一直持续下去,当自旋一定次数的时候,还没有获取到锁,就会进入阻塞,该锁膨胀为重量级锁。重量级锁会让他申请的线程进入阻塞,性能降低。
自旋锁
在Java中,自旋锁是指尝试获取锁的线程不会立即阻塞,而是采用循环的方式去尝试获取锁,这样的好处是减少线程上下文切换的消耗,缺点是循环会消耗CPU。
并发集合
List
ArrayList
LinkedList
CopyOnWriteArrayList
Set
HashSet
CopyOnWriteArraySet
ConcurrentSkipListSet
Queue
BlockingQueue
LinkedBlockingQueue
DelayQueue
PriorityBlockingQueue
ConcurrentLinkedQueue
TransferQueue
BlockingDeque
LinkedBlockingDeque
ConcurrentLinkedDeque
Map
HashMap
ConcurrentMap
ConcurrentHashMap
ConcurrentSkipListMap
ConcurrentNavigableMap
Collections
SynchronizedList
线程池使用
核心参数(ThreadPoolExecutor)
corePoolSize
核心线程池大小
maximumPoolSize
线程池中允许的最大线程数
keepAliveTime
当线程数大于核心线程数的时候,
线程在最大多长时间内没有接到新任务就会终止释放
最终线程池维持在核心线程池(corePoolSize)大小
线程在最大多长时间内没有接到新任务就会终止释放
最终线程池维持在核心线程池(corePoolSize)大小
TimeUnit
时间单位
BlockingQueue
阻塞队列,用来存储等待执行的任务,|
若当前对线程的需求超过了corePoolSize大小
就会存放在此,等待空闲线程执行!
若当前对线程的需求超过了corePoolSize大小
就会存放在此,等待空闲线程执行!
ArrayBlockingQueue
LinkedBlockingQueue
SynchronousQueue
PriorityBlockingQueue
DelayQueue
LinkedTransferQueue
LinkedBlockingDeque
threadFactory
创建线程的工厂
handler
拒绝策略,若线程池满了,线程池会使用拒绝策略
运行流程
线程池创建,准备好 core 数量的核心线程,准备接受任务
新的任务进来,用 core 准备好的空闲线程执行
core 满了,就将再进来的任务放入阻塞队列中。
空闲的 core 就会自己去阻塞队
列获取任务执行
空闲的 core 就会自己去阻塞队
列获取任务执行
、阻塞队列满了,就直接开新线程执行,最大只能开到 max 指定的数量
max 都执完后,(max-core) 数量空闲的线程会在 keepAliveTime 指定的时间后自
动销毁。最终保持到 core 大小
动销毁。最终保持到 core 大小
如果线程数开到了 max 的数量,还有新任务进来,就会使用 reject 指定的拒绝策
略进行处理
略进行处理
所有的线程创建都是由指定的 factory 创建的。
线程
创建方式
继承Thread类
实现Runable接口
实现Calable接口+FuturaTask
线程的六种状态
NEW
线程刚创建,还未启动
RUNNABLE
可运行状态,由线程调度器安排执行
Ready
Running
WAITING
等待被唤醒
TIMED WAITING
隔一段时间后自动唤醒
BLOCKED
被阻塞,正在等待锁
TERMINATED
线程结束
常见四种线程池
newCacheThreadPool
newFixedTheadPool
newScheduledThreadPool
newSingleThreadExecutor
为啥要用线程池
降低资源的消耗
通过重复利用已经创建好的线程降低线程的创建和销毁带来的损耗
提高响应速度
因为线程池中的线程数没有超过线程池的最大上限时,有的线程处于等待分配任务
的状态,当任务来时无需创建新的线程就能执行
的状态,当任务来时无需创建新的线程就能执行
提高线程的可管理性
线程池会根据当前系统特点对池内的线程进行优化处理,减少创建和销毁线程带来
的系统开销。
无限的创建和销毁线程不仅消耗系统资源,还降低系统的稳定性,使用线程池进行统一分配
的系统开销。
无限的创建和销毁线程不仅消耗系统资源,还降低系统的稳定性,使用线程池进行统一分配
使用多线程的问题
内存泄露
静态集合类
数据库连接、网络连接、IO连接等
变量不合理的作用域
内部类引用外部类
死锁
死锁发生条件
互斥条件:一个资源每次只能被一个线程使用
请求与保持条件:一个线程因请求资源而阻塞时,对已获得的资源保持不放
不剥夺条件:进程已经获得的资源,在未使用完之前,不能强行剥夺
循环等待条件:若干线程之间形成一种头尾相接的循环等待资源关系
避免死锁
只要破坏四个条件其中之一即可
推荐加锁方式
永远只在更新对象的成员变量时加锁
永远只在访问可变的成员变量时加锁
永远不在调用其他对象的方法时加锁
上下文切换
并发编程的三大特性
可见性
volitile
保障线程可见性,线程本地缓存互相保持同步
cache line
64 bits
MESI cache
有序性
对象的创建过程
this逸出
中间状态
不要在构造方法start线程
汇编码
乱序存在的条件
as-if-serial
不影响单线程的最终一致性
原子性
0 条评论
下一页