1.1 时间片轮转
时间片轮转算法的基本思想是,系统将所有的就绪进程按先来先服务算法的原则,排成一个队列,每次调度时,系统把处理机分配给队列首进程,并让其执行一个时间片。当执行的时间片用完时,由一个计时器发出时钟中断请求,调度程序根据这个请求停止该进程的运行,将它送到就绪队列的末尾,再把处理机分给就绪队列中新的队列首进程,同时让它也执行一个时间片。
1.2 线程的优势
风险:安全性问题(数据不对,资源冲突),活跃性问题(死锁,活锁,线程饿死,时序问题),性能问题(性能反而下降,资源消耗过高)
好处:充分利用cpu的资源、加快用户响应的时间,程序模块化,异步化
1.3 线程安全性
要编写线程安全的代码,共享的和可变的状态的访问。
当获取与对象关联的锁时,并不能阻止其他线程访问该对象,某个线程在获得对象的锁后,只能阻止其他线程获得同一个锁。之所以每个对象都有一个内置锁,只是为了免去显式创建锁对象。每个共享的和可变的变量都应该只由一个锁来保护。
一种常见的加锁约定是,将所有的可变状态都封装在对象内部,并通过对象的内置锁对所有访问可变状态的代码路径进行同步,使得在该对象上不会发生并发访问。
并发小技巧:
- 可变状态是至关重要的。所有的并发问题都可以归结为如何协调对并发状态的访问。可变状态越少,就越容易确保线程安全性。
- 尽量将域声明为final类型,除非需要他们是可变的。
- 不可变对象一定是线程安全的
- 封装有助于管理复杂性
- 用锁来包含每个可变变量
- 在现有的线程安全类中添加功能
- 当保护同一个不变形条件中的所有变量时,要使用同一个锁
- 在执行复合操作时,要持有锁
- 在设计过程中考虑线程安全,或者再文档中明确指出它不是线程安全的
- 将同步策略文档化
- 栈封闭:所有的变量都是在方法内部声明的,这些变量都处于栈封闭状态。
- 创建无状态的类
- 安全的发布类中持有的成员变量,特别是对象的引用,如果这个成员对象不是线程安全的,通过get等方法发布出去,会造成这个成员对象本身持有的数据在多线程下不正确的修改,从而造成整个类线程不安全的问题。
1.4 性能和思考
线程间上下文切换和内存同步,是影响性能的主要因素。
锁优化
1:减少锁持有时间
例如:对一个方法加锁,不如对方法中需要同步的几行代码加锁;
2:减小锁粒度
例如:ConcurrentHashMap采取对segment加锁而不是整个map加锁,提高并发性;
3:锁分离
根据同步操作的性质,把锁划分为的读锁和写锁,读锁之间不互斥,提高了并发性。
4:锁粗化
这看起来与思路1有冲突,其实不然。思路1是针对一个线程中只有个别地方需要同步,所以把锁加在同步的语句上而不是更大的范围,减少线程持有锁的时间;
而锁粗化是指:在一个间隔性地需要执行同步语句的线程中,如果在不连续的同步块间频繁加锁解锁是很耗性能的,因此把加锁范围扩大,把这些不连续的同步语句进行一次性加锁解锁。虽然线程持有锁的时间增加了,但是总体来说是优化了的。