多线程与高并发编程进阶(一)

前言:

使用多线程的目的:
充分利用CPU资源,提高程序运行速度
使用多线程面临的挑战:
上下文切换、死锁、计算机软硬件资源的限制等问题
结论:
不是一味地开启线程就能够让程序最大限度地并发执行,以及提升执行速度,想利用多线程提升程序运行速度需要结合实际情况考虑上下文切换、死锁以及软硬件资源限制等因素,只有这样才能够合理地使用多线程来提升程序运行效率


上下文切换:

通过为每个线程分配CPU时间片来实现多线程执行代码的机制,即使在单核处理器的情况下,依旧可以使用多线程执行代码;

时间片就是CPU的执行时间,通过分配时间片给各个线程,用以执行对应的代码;CPU时间片一般很短(几十毫秒左右),通过不停地切换线程来使用对应的CPU时间片,这样带来的感觉就是这些线程似乎在并行执行;

由于一个线程分配得到的CPU时间片用完,需要切换下一个线程继续使用CPU的计算资源,但是在这之前,线程需要将当前的状态进行保存,以便下次再次获得CPU时间片时可以加载对应的状态以继续执行剩下的任务(代码),所以在保存线程的当前状态到加载下一个线程的状态这个过程就是一次上下文切换;注意:上下文切换需要耗费时间,会影响多线程程序的执行效率,在使用多线程时需要考虑这一点;

减少上下文切换的途径:

  1. 无锁并发编程:锁的竞争会带来线程上下文的切换
  2. CAS算法:CAS算法在数据更新方面,可以达到锁的效果
  3. 使用最少线程:避免不必要的线程等待
  4. 使用协程:单线程完成多任务的调度和切换,避免多线程

无锁并发编程,避免由于使用锁导致的上下文切换,比如对共享数据进行分段,不同的线程处理不同段的数据;比如JDK提供的ConcurrentHashMap;
CAS算法,比如Java的Atomic包中的原子类使用CAS算法来更新数据的状态,从而避免了锁的使用;
使用最少线程,即不创建不需要的线程,按需使用线程,避免过多不必要的线程等待(等待获取锁);
使用协程,在单线程中进行多任务的调度以及维持多个任务之间的切换(实质上已经和多线程无关);


死锁:

多个线程互相等待已经被对方线程正在占用的锁,导致陷入彼此等待对方释放锁(但是又都不释放已占用的锁)的状态(死锁),最终会导致系统功能的不可用;

避免死锁的常见措施:

  1. 避免一个线程中同时获取多个锁
  2. 避免一个线程在一个锁中获取其他的锁资源
  3. 考虑使用定时锁来替换内部锁机制,如lock.tryLock(timeout),避免由于异常发生导致的锁释放失败的情况发生
  4. 对于数据库锁,需要在同一个数据库连接中完成同一个锁的获取与释放,避免出现释放锁失败的情况

计算机资源的限制:

由于计算机上的硬件、软件资源的限制,导致多线程并不能发挥优势,相反会对程序运行性能带来影响;

常见的限制有:

  1. 计算机的带宽、上传下载速度以及硬盘读写速度、CPU的处理速度等硬件资源上的限制
  2. 数据库连接数、socket连接数等软件资源上的限制

解决的方法:

  1. 使用数据库连接池以及socket链接池等技术来降低创建连接的时间以及提高连接的复用率
  2. 根据不同的资源限制调整程序的并发度(线程数量)根据不同的资源限制调整程序的并发度(线程数量)

建议:
尽量使用JDK提供的并发容器以及并发工具类来解决并发问题,这些JDK中的并发工具和容器是经过优化和验证的,所以一般不会出现上述的问题;

你可能感兴趣的:(Java多线程,多线程与并发编程)