转载请注明出处 http://www.paraller.com
原文排版地址 点击获取更好阅读体验
多线程简介
进程出现的原因
资源利用率
程序在等待操作执行完成的空档,运行另一个程序 提高资源利用率。
公平性
用户对于计算机的使用权,通过粗粒度的时间分片 使得用户和程序共享资源程序。
便利、效率性
一个任务分解多个子任务,必要时通信共同完成任务。
异步事件的简单处理
对于单线程而言,如果要高效处理多个操作,必须使用非阻塞IO,复杂且易错,如果能够为每个请求分配线程将降低开发的难度
线程允许在同一个进程中同时存在多个程序控制流,线程会共享进程中的资源;每个线程有各自的 程序计数器、栈、局部变量。同一个进程中的多个线程也可以被同时调度到多个CPU上运行。
多线程的风险
安全性
对共享的可变的变量进行无法预测顺序的操作,将会造成与预期不一致的结果。
活跃性
某个操作无法继续进行下去:A线程占有资源长时间不释放,导致B线程无法继续操作
额外的性能开销:
- 线程调度器,频繁的上线文切换
线程共享数据/使用同步机制:抑制编译器的优化、缓存失效、共享内存总线的同步流量
线程安全性
核心在于: 避免多线程同时访问可变状态
核心知识点
- 编写线程安全代码的核心在于:对
共享
可变的
状态访问操作进行管理 - 状态可以是数据,也可以是其他依赖的对象(比如HashMap自身或者Map.Entry对象)
- 存在多个线程访问,多个线程修改,
必须使用同步
- 线程安全类的定义: 多个线程访问某个类时,类始终表现出正确的行为
竞态条件: 于不恰当的执行顺序,而出现不正确的结果.
- 常见类型 "先检查再执行"
- 通过原子性原理解决 ,比如 复合操作.
- concurrent包中包含了原子变量类.
加锁机制
- 同步代码块将包括两个部分: 一个作为锁的对象引用;一个作为由这个锁保护的代码块
- 正常路径退出 和 抛出异常退出,都将释放锁
-
重入:线程获取自己持有的锁,请求会获得成功
- 获取锁的操作粒度是 "线程",不是"调用"
- 一种实现方法是为每个锁关联 一个计数器和一个所有者线程.
- 通过缩小同步代码块的范围,可以在性能和同步间找到平衡
- 一种常见的加锁约定:通过将状态封装在对象内,通过同一个对象锁,对状态的读写访问都添加锁来确保安全
-
用同步来协调对一个变量的访问
- 在访问这个变量的所有位置上都需要同步
- 所有位置上都必须使用同一个锁
- 不仅写入需要,读取也需要同步
- 当执行长时间计算和无法快速完成的操作,时,一定不要持有锁或者要设置定时锁(Redisson)
活跃性和性能
- 通过缩小同步代码块的范围来保持两者平衡
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。