线程和进程区别
进程是程序执行的过程,线程是轻量级进程,程序执行的分支
- 进程是资源分配单位,线程是资源调度单位;
- 线程是进程划分成的更小的运行单位;
- 进程相互独立,同一进程中的线程可能会相互影响;
- 线程执行开销小,但不利于资源的管理和保护;而进程正相反
为什么要用多线程
- 提高程序运行效率(将大任务分解成互不依赖的小任务,每个任务用一个线程执行,大任务的执行时间可以降到等于执行时间最长的小任务时间);
- 提高cpu利用率(线程池,多个同类任务并发执行,并发数取决于);
什么是线程安全
- 多个线程并发执行,结果正确
- 三个原则:原子性、可见性、有序性
怎么思考线程安全问题
- 能不能保证操作的原子性,考虑使用Atomic包
- 能不能保证操作的可见性,考虑使用volatile
- 批量操作,避免重复创建线程,使用线程词
- 集合,考虑concurrent包下集合类
- 锁,synchronized,lock包
死锁
当前线程拥有其他线程需要资源,当前线程还需要其他线程资源,都不释放拥有的资源
产生死锁的必要条件
- 互斥
- 请求与保持
- 不可剥夺
- 循环等待
避免死锁
- 一次性获取所有
- 等待一定时间主动释放占有资源
- 按某种顺序请求资源(哈希,银行家算法……)
CAS
比较并交换,如果等于原来的值就更新为新的值,原子性操作,对应到CPU指令为cmpxchg
为什么要用CAS
并发环境中,操作共享变量时,会产生线程安全问题,可以使用Synchronized对共享变量加锁,确保只有一个线程能够修改共享变量,但是有些情况下这样开销太大,例如对一个值进行累加,这种情况下可以用Atomic包里的类,这些类底层就是使用CAS实现的,而CAS相当于没有加锁,多个线程可以直接操作,在实际修改时在判断是否成功,比锁更高效
CAS缺点
带来ABA问题
ABA问题:CAS更新时只比较当前值和内存值是否相等,但是假设线程A读到当前值是1,可能线程B把值改为2,线程C又把值改为1,这是线程A修改这个变量是可以成功的,对于A来说值未改变,但是这样是不合理的,值已经被B、C修改了
解决方案:使用AtomicStampedReference类,原理是增加了版本号,即比较内存值+版本号是否一致
为什么LongAdder 对象,比 AtomicLong 性能更好(减少乐观锁的重试次数)
AtomicLong 是多线程操作一个资源,高并发时,只有一个成功,其他的不断自旋,浪费cpu资源
LongAdder将要操作的资源分散到cell数组中,每个线程对cell的value进行CAS操作,大大降低失败次数