【多线程】Java高并发基础

时间:2021-11-22 15:26:03

一、摩尔定律的失效和多线程的发展
摩尔定律的思想是,预计18个月会将芯片的性能提高一倍。但是在2004年,Intel CEO宣布Intel彻底取消4G Hz计划,至此摩尔定律在芯片上的发展已经失效了。如果计算机没有办法继续提高单个CPU的性能,那么就在一个CPU里面塞很多的核进去,因此多核CPU兴起。
在单核时代,多线程就有很广泛的应用,这时候多线程大多用于降低阻塞。多线程的作用不只是用作并行计算,还可以提高CPU的利用率。

二、线程的介绍
线程是进程里面更细微的执行单元,而多线程是为了使得多个线程并行的工作以完成多项任务,以提高系统的效率,线程是在同一时间需要完成多项任务的时候被实现。

线程的基本操作

  1. 线程刚刚新建出来,那它的状态就是new的状态,new状态的线程其实并没有开始工作,它只是一个静态的实体
  2. 当调用实例的start方法之后,线程才真正的被启动。线程被启动之后状态变成RUNNABLE状态,表示线程可以执行了,一切准备就绪(但RUNNABLE状态并不一定表示这个线程一定在CPU上执行,有没有真正在执行,取决于物理CPU的调度)
  3. 如果一个线程它的所有工作都做完了,那么自然它就会被终结掉,状态就处于TERMINATED状态,表示线程任务执行结束
  4. 有些时候线程在执行过程中不可避免要去申请某些锁,比如要求申请某些对象的一个定时器,比如调用了synchronized方法,这个时候线程可能会被阻塞住
  5. 如果一个线程在执行过程中调用了wait,那么这个进程就会变成等待状态WAITING,进入等待状态的线程它会等待另一些线程对它进行通知,它被通知之后就会从WAITING状态变为RUNNABLE状态,继续执行
  6. 等待状态分为两种,一种是无限期的等待,一种是有限期的等待(TIMED.WAITING,比如我就等待10秒,如果过了十秒没有线程通知,就把状态由WAITING变为RUNNABLE)

使用线程的好处

  1. 使用线程可以把占据长时间的程序中的任务放到后台去处理;
  2. 用户界面可以更加吸引人,这样比如用户点击了一个按钮去触发某些事件的处理,可以弹出一个进度条来显示处理的进度;
  3. 程序的运行速度可能加快;
  4. 在一些等待的任务实现上如用户输入、文件读写和网络收发数据等,线程就比较容易了。在这种情况下,我们可以释放一些珍贵的资源如内存占用等等。

守护线程:
守护线程是指在后台默默运行的一些系统,为整个系统的运行提供一些支撑服务,能帮助系统做一些后台运维方面的事情,可能跟业务关系不是很大。
如果主线程main函数结束了,或者是main函数所开启的一些非守护线程结束了,这个时候我们可以认为这个Java程序已经没有存在的必要了(因为程序存在的目的就是执行非守护线程)。非守护线程会执行一些业务逻辑,而守护线程往往只是起到一些辅助作用

线程优先级:
线程是有优先级的,具有优先级高的线程更容易抢占系统资源,更快的执行。

三、一些重要的概念
同步和异步
同步和异步是对方法调用而言的。同步调用的等着方法调用结束之后返回结果,方法执行多久就等待多久
异步调用是会立即得到一个返回结果,但并不表示调用完成了,而是在后台启用一个线程慢慢的去做事情,但不影响后续调用事件的继续。

并发和并行
并行是两个线程或进程同时执行。并发是指一会儿做这件事情 一会儿做另一件事情(它有一个调度的过程)
对于单个CPU而言不可能出现并行,它同一时间只能处理一个线程(进程);对于多个CPU而言就是可以实现并行的

临界区
临界区用来表示一种公共资源或者是共享数据,可被多个线程使用。
但每一次只能有一个线程使用它,因为如果不加限制,很可能因为多个线程进入临界区,把数据改坏掉。所以临界区每一次只能有一个进程进去。
如果一个线程进入临界区,其他线程也想进入临界区,则需要进入阻塞队列进行等待。

阻塞和非阻塞
阻塞和非阻塞通常用来形容多线程间的相互影响。比如一个线程进入了临界区,其他线程不能进入临界区,需要在临界区之外进行等待,导致线程挂起,这种情况就是阻塞。
阻塞的意思就是指这个线程在操作系统层面,被挂起。
如果占用资源的线程一直不愿意释放资源,那么其他所有阻塞在这个临界区的线程都不能工作。
非阻塞线程是允许多个线程同时进入临界区。只要保证不把数据改坏就可以了。

死锁、饥饿和活锁:
对于阻塞队列来讲,在临界区就有可能发生死锁的现象。死锁是使程序卡死在那儿,没办法继续往下走,没办法提供服务了
死锁虽然不是一个好现象,但死锁属于一个静态的问题,一旦出现死锁,所有线程都卡死,CPU的占用率也是0,不会占用CPU。
与死锁对应的是活锁,活锁的例子是电梯遇人,一个要进去,一个要出去,两人一直相对左右左右的避让,结果一直进不去。活锁是一个动态的问题,它不像死锁那样一直不动,性能会受到非常严重的影响
有的线程优先级很高,有的线程优先级很低,在调度的时候会调度不到优先级很低的线程,一直一直持续下去会被饿死。
比如同时阻塞在一个临界区上,结果操作系统只调度优先级高的线程,因为优先级很低所以调度不到,就不能往下继续执行。这样下去就会被饿死,因为分不到足够的资源

并发级别:
阻塞:一个线程进入临界区之后,其他线程必须在临界区外进行等待
非阻塞:
无障碍:无障碍是一种最弱的非阻塞调度;*出入临界区,无竞争时有限步骤内完成操作;有竞争时回滚数据(宽进严出的策略)
无锁:无锁在无障碍的基础上,加一个约束,就是 保证有一个线程可以胜出
无等待:首先要保证无锁;要求所有进入临界区的线程都能在有限步内完成;无饥饿。是并发的*别。