Skip to content

Latest commit

 

History

History
90 lines (63 loc) · 6.45 KB

并发编程基础知识.md

File metadata and controls

90 lines (63 loc) · 6.45 KB

JUC简介

JUC的全称是java.util.concurrent工具包的简称,是一个用来处理线程的工具包

进程和线程

  • 进程 是计算机中的程序关于某数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位,是操作系统结构的基础。
  • 线程 是操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位。一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务

总结

  • 进程是应用程序的运行实例,而线程是进程内的运行实例
  • 进程是操作系统资源分配的基本单位,而线程是操作系统执行调度的基本单位
  • 我们可以将进程比作我们的公司,然后其中的员工就是线程,公司作为协调资源的基本单位,其所要执行的任务和完成的工作都是细化到具体的下面的员工进行完成的。
  • 操作系统在分配资源时是把资源分配给进程的, 但是 CPU 资源⽐较特殊,它是被分配到线程的,因为真正要占⽤CPU运⾏的是线程,所以也说线程是 CPU分配的基本单位

并发编程的核心问题

并发编程的三个核心问题

  1. 分工
    • 分工的概念就是如何将任务进行有效的拆解分给线程
    • Fork/Join 框架就是一种分工模式
  2. 同步
    • 同步的概念是指线程之间如何协作
    • CountDownLatch就是一种典型的同步方式
  3. 互斥
    • 互斥的概念也是我们最常见的,也就是如何保证再同一个时刻只有一个线程访问共享资源
    • 可重入锁则是一种互斥手段

并发和并行

这里还有一个常见的模式是串行,也就是说一个任务接着一个任务进行处理,前面处理完成之后后面的才能处理,这里就稍微记录一下。

并发

并发是指多个任务同时进行,但是这些任务不是同时开始,而是按照一定的顺序进行,比如一个任务A执行到一半的时候,此时任务B开始执行,当任务A执行完成之后,任务B继续执行,这种任务A和任务B是同时进行的,但是不是同时开始,所以就称为并发。

并行

并行是指多个任务同时开始执行,比如同时开始执行任务A和任务B,当任务A执行完成之后,任务B继续执行,这种任务A和任务B是同时进行的,所以就称为并行。更细化的是多进程可 以同时运行或者多指令可以同时运行。这里的并发描叙的是一种现象,但实际上,对于单核心 CPU 来说,同一时刻只能运行一个线程。所以,这里的"同时运行"表示的不是真的同一时刻有多个 线程运行的现象,这是并行的概念,而是提供一种功能让用户看来多个程序同 时运行起来了,但实际上这些程序中的进程不是一直霸占 CPU 的,而是执行一会停一会。

关键点 是否是同时执行的,并行强调的是同一个时间范围内,但并发强调的是同一个时刻

用户线程和守护线程

用户线程和守护线程的区别在于

  1. 用户线程是程序员创建的,而守护线程是 JVM 创建的。
  2. 用户线程是平时用到的普通线程, 自定义线程,守护线程运行在后台,是一种特殊的线程,比如垃圾回收,应用指标统计。

当主线程结束后,用户线程还在运行,JVM 存活。如果没有用户线程,都是守护线程,JVM 结束

区别之⼀是当最后⼀个⾮守护线程束时,JVM会正常退出,⽽不管当前是否存在守护线程,也就是说守护线程是否结束并不影响JVM退出。换⽽⾔之,只要有⼀个⽤户线程还没结束,正常情况下JVM就不会退出,与普通线程相比,守护线程的生命周期与主线程或其他非守护线程的生命周期无关。当所有非守护线程结束时,守护线程会自动被终止,不管它是否执行完毕。

守护线程注意事项

守护线程的设置 setDaemon(true) 必须要放在线程的 start() 之前,否则程序会报错。也就是说在运行线程之前,一定要先确定线程的类型,并且线程运行之后是不允许修改线程的类型的。

创建守护线程

  • 通过 Thread.setDaemon(true) 方法将线程设置为守护线程
  • 将线程池设置为守护线程
    • 使用线程工厂 ThreadFactory 来设置了(线程池中的所有线程都是通过线程工厂创建的)

线程间的通信

  • volatile和synchronized关键字
    • 基于 volatile 关键字来实现线程间相互通信是使用共享内存的思想。大致意思就是多个线程同时监听一个变量,当这个变量发生变化的时候 ,线程能够感知并执行相应的业务。这也是最简单的一种实现方式
  • 等待/通知机制(wait()/notify()/notifyAll())
    1. wait():让当前线程处于等待状态,并释放当前拥有的锁;
    2. notify():随机唤醒等待该锁的其他线程,重新获取锁,并执行后续的流程,只能唤醒一个线程;
    3. notifyAll():唤醒所有等待该锁的线程(锁只有一把,虽然所有线程被唤醒,但所有线程需要排队执行)
  • 使用 Locks 和 Condition
    1. await():对应 Object 的 wait() 方法,线程等待;
    2. signal():对应 Object 的 notify() 方法,随机唤醒一个线程;
    3. signalAll():对应 Object 的 notifyAll() 方法,唤醒所有线程。
  • 使⽤Thread.join()
  • 使⽤ThreadLocal
  • 使用 Exchanger
    • Exchanger 是一个用于线程间交换数据的同步点。两个线程可以在此同步点交换数据,Exchanger 的 exchange() 方法用于在两个线程之间交换数据。
  • 使用JUC工具类 CountDownLatch,Semaphore等
  • 基本 LockSupport 实现线程间的阻塞和唤醒 LockSupport 是一种非常灵活的实现线程间阻塞和唤醒的工具,使用它不用关注是等待线程先进行还是唤醒线程先运行,但是得知道线程的名字。
    1. LockSupport.park():休眠当前线程。
    2. LockSupport.unpark(线程对象):唤醒某一个指定的线程。
    3. LockSupport 存在的必要性:前两种方法 notify 方法以及 signal 方法都是随机唤醒,如果存在多个等待线程的话,可能会唤醒不应该唤醒的线程,因此有 LockSupport 类下的 park 和 unpark 方法指定唤醒线程是非常有必要的。