主要还是站在线程的角度看待并发

什么是并发?什么是并行?

一核对应一个线程,但是一个线程除非让它遇到任务阻塞,不然一个线程的处理能力还是很强的,双核的性能提高不超过1.87倍。

并发

所谓的并发并不局限在线程,在单处理器多道程序设计系统中,进程交替执行的情况下也是一种并发的实例。并且无论是拿进程来讲还是线程来讲,并发都是多个事件占用资源(cpu的时间片)的一种动作。而线程就可以更细粒度的去调度cpu的资源。
在这里引入两个名词:逻辑控制流,并发流
逻辑控制流:在程序的执行时都会存在一个叫程序计数器(PC)的东西,这个东西描绘了一个程序在系统中的执行轨迹,多个PC值组合在一起就是一个逻辑控制流
并发流:一个逻辑流的执行在时间上与另一个流重叠,称为并发流。当流X和Y互相并发,当且仅当X在Y开始之后和Y结束之前开始,那么就称X和Y是并发状态的。注:并发流的思想与流运行的处理器核数或者计算机数无关,如果两个流在运行时间上有重叠,那么他们就是并发的
下图中同一个操作系统下,那么A和B和C这三个进程都是处于并发状态的。
image.png
那么在java中我们称为的并发也就是使用多线程并且每个线程流处于并发的时候去操作同一份资源(变量,文件等等)

并行

并行也有一个特殊的名词称为并行流,并行流是并发流的一个真子集,也就是两个流并发的运行在不同的处理器核或者计算机上,那么就称它们为并行流。

什么是并发编程都包含哪些东西

用多线程来编程,实现复杂的系统功能,让多个线程同时运行,干各种事情,最终完成一套复杂系统需要干的所有的事。

  • 控制多线程实现系统功能
  • Java内存模型以及volatile关键字
  • 线程同步以及通信
  • 锁优化
  • 并发编程设计模式:基于多线程实现复杂系统架构
  • 并发包以及线程池
  • 案例、大量的案例(脱胎于真实的复杂分布式系统)

线程的状态

在操作系统看出是存在6种状态
image.png
进入java的Thread类中查看,也是遵循操作系统中来进行设计

  1. public enum State {
  2. NEW,
  3. RUNNABLE,
  4. BLOCKED,
  5. WAITING,
  6. TIMED_WAITING,
  7. TERMINATED;
  8. }

线程的状态流转

image.png

New

继承Thread类或者实现Runnable,创建该线程对象。

Ready

1.调用线程的start()方法,此线程进入就绪状态。
2. 当前线程sleep()方法结束,其他线程join()结束,等待用户输入完毕,某个线程拿到对象锁,这些线程也将进入就绪状态。
3.当前线程时间片用完了,调用当前线程的yield()方法,当前线程进入就绪状态。
4.锁池里的线程拿到对象锁后,进入就绪状态。

Running运行中状态

线程调度程序从可运行池中选择一个线程作为当前线程时线程所处的状态。这也是线程进入运行状态的唯一一种方式。

BLOCKED(阻塞状态)

阻塞状态是线程阻塞在进入synchronized关键字修饰的方法或代码块(获取锁)之前时的状态。

WAITING(等待状态)

调用sleep或是wait方法后线程处于WAITING状态,等待被唤醒。

TIMED_WAITING(等待超时状态)

调用sleep或是wait方法后线程处于TIMED_WAITING状态,等待被唤醒或时间超时自动唤醒。

TERMINATED(终止状态)

  1. 当线程的run()方法完成时,或者主线程的main()方法完成时,当前这个java线程就可以认为是结束了。这个线程对象也许是活的,但是,它已经不是一个单独执行的线程。线程一旦终止了,就不能复生。
    2. 在一个终止的线程上调用start()方法,会抛出java.lang.IllegalThreadStateException异常。

    死锁

    “死锁” 是两个或两个以上的线程在执行过程中,互相持有对方所需要的资源,导致这些线程处于等待状态,无法继续执行。若无外力作用,它们都将无法推进下去,就进入了“永久”阻塞的状态。

    产生原因

  • 互斥,共享资源 X 和 Y 只能被一个线程占用;
  • 占有且等待,线程01 已经取得共享资源 X,在等待共享资源 Y 的时候,不释放共享资源 X;
  • 不可抢占,其他线程不能强行抢占线程01 占有的资源,只能由01线程主动释放该资源;
  • 循环等待,线程01 等待线程02 占有的资源,线程02 等待线程01 占有的资源,就是循环等待。

避免死锁

  1. “互斥”是没有办法避免的,对于“占用且等待”这个条件,我们可以一次性申请所有的资源,这样就不存在等待了。

    一次性获取相关对象的所有锁,让当前线程可以安全的执行下去

  2. 对于“不可抢占”这个条件,占用部分资源的线程进一步申请其他资源时,如果申请不到,可以在一定时间后,主动释放它占有的资源,这样就解决了不可抢占这个条件。

    使用Lock,当一段时间后主动去执行lock.unlock,这点是synchronized做不到的。

  3. 对于“循环等待”,我们可以靠按“次序”申请资源来预防。所谓按序申请,就是给资源设定顺序,申请的时候可以先申请序号小的资源,再申请序号大的,这样资源线性化后,自然就不存在循环等待了。

    获取锁做一个有序的操作

image.png