为了让程序运行的更快,引入多线程。并不是启用更多的线程就会提高程序的运行速度。并发编程会有很多挑战。比如上下文切换,死锁问题。
上下文切换
cpu是通过时间片流转的方式分配线程完成任务。当一个时间片完成之后,会切换到另一个线程的任务,此时就需要保存刚刚未完成的任务的环境等,操作系统的知识 该东西被存在pcb中 ,以便在切回这个线程时可以继续,这个过程被称为上下文切换。
上下文如何切换,便是一个多线程可以高效完成任务的关键。
多线程不一定比单线程快的,并发累计操作不超过百万时,比串行慢。原因就是上下文的切换和创建线程的开销。
关于上下文的测试 有几个工具。
Lmbench3 可以测试上下文切换的时长
vmstat 测试上下文切换的次数
减少上下文的切换
无锁并发编程,CAS算法,使用最少线程 ,使用协程
无锁并发编程
使用Hash 将数据的id分段, 不同的线程处理不同的数据
CAS算法
Atomic的CAS 算法 ,不需要加锁 。乐观锁实现的一种方法,但是有可能长时间自旋,增加cpu的开销。 原理就是在更新数据之前要检查是否跟之前的值相同 ,是则更新原值。理论上需要三个数值,一个内存值,预期的原值,和新值,如果预期的原值和内存值相同,更新新值到内存中。
cas存在的问题:
- ABA :就是说 因为CAS算法会在更新前比较值,比较的时候两个值看起来相同,比方说都是a ,但是有可能在这个过程中他已经先被改成b 又改回 a 那么cas就会认为它是没有被改变的,由此出现问题。这个问题的解决方法就是利用版本号机制,就是说每次更改留下一个版本号。
- 长时间循环自旋:就是说CAS可能长时间不成功,就会自旋 增加cpu的开销。
- 他只能同时保证一个共享变量的原子操作。 但是从1.5之后 一个AomicReference类的出现保证引用对象的原子性,可以把多个共享变量放在同一个对象中进行CAS操作
使用最少线程
避免创建没必要的线程。
协程
在单线程里实现多任务的调度,维持多个任务的切换。
死锁
按照操作系统的定义:当一个线程无限期等待另一个线程所占有的不会释放的资源,就会引起死锁问题。出现死锁时 可以用dump线程来查看到底是哪出现了死锁
避免死锁的几个方法:
- 避免线程同时获取多个锁
- 避免一个线程在锁内同时占用多个资源
- 使用定时锁
- 对于数据库锁,加锁解锁要在一个数据库连接中
资源的限制
比如一些硬件的问题了。