JUC简介

JUC 就是 java.util .concurrent 工具包的简称。这是一个处理线程的工具包,它在 JDK 1.5 开始出现。

image.png

进程与线程

1. 进程(Process)

是计算机中的程序关于某数据集合上的一次运行活动,是系 统进行资源分配和调度的基本单位,是操作系统结构的基础。 在当代面向线程 设计的计算机结构中,进程是线程的容器。程序是指令、数据及其组织形式的 描述,进程是程序的实体。是计算机中的程序关于某数据集合上的一次运行活 动,是系统进行资源分配和调度的基本单位,是操作系统结构的基础。程序是 指令、数据及其组织形式的描述,进程是程序的实体。

2. 线程(thread)

是操作系统能够进行运算调度的最小单位。它被包含在进程之 中,是进程中的实际运作单位。一条线程指的是进程中一个单一顺序的控制流, 一个进程中可以并发多个线程,每条线程并行执行不同的任务。

3. 总结

进程:指在系统中正在运行的一个应用程序;程序一旦运行就是进程;进程——资源分配的最小单位。

线程:系统分配处理器时间资源的基本单元,或者说进程之内独立执行的一个 单元执行流。线程——程序执行的最小单位。

4. 生活实例

使用QQ,查看进程一定有一个QQ.exe的进程,我可以用qq和A文字聊天,和B视频聊天,给C传文件,给D发一段语音。

Java默认有几个线程

默认有2 个线程,分别是 mian和GC

Java 真的可以开启线程吗

不能,实际还是JVM底层调用操作系统的线程

线程的状态

线程状态枚举类

1. Thread.State

  1. public enum State {
  2. /** 新生 */
  3. NEW,
  4. /** 运行 */
  5. RUNNABLE,
  6. /** 阻塞 */
  7. BLOCKED,
  8. /** 等待 */
  9. WAITING,
  10. /** 超时等待 */
  11. TIMED_WAITING,
  12. /** 终止 */
  13. TERMINATED;
  14. }

2. BLOCKED(阻塞),WAITING(等待),TIMED_WAITING(超时等待)的区别

2.1 BLOCKED(阻塞)

Java文档官方定义BLOCKED状态是:“这种状态是指一个阻塞线程在等待monitor(对象监视器)锁。”

真实生活例子:今天你要去面试。这是你梦想的工作,你已经盯着它多年了。你早上起来,准备好,穿上你最好的外衣,对着镜子打理好。当你走进车库发现你的老婆已经把车开走了。在这个场景,你只有一辆车,要怎么办?在真实生活中,可能会打架。 现在因为你老婆把车开走了你被BLOCKED了。你不能去参加面试。

这就是BLOCKED状态。用技术术语讲,你是线程T1,你老婆是线程T2,而锁是车。T1被BLOCKED在锁(例子里的车)上,因为T2已经获取了这个锁。

小贴士:当线程调用Object#wait()方法进入一个synchronized块/方法或重进入一个synchronized锁/方法时会等待获取monitor锁。

2.2 WAITING(等待)

Java文档官方定义WAITING状态是:“一个线程在等待另一个线程执行一个动作时在这个状态”

真实生活例子:再看下几分钟后你的老婆开车回家了。现在你意识到快到面试时间了,而开车过去很远。所以你拼命地踩油门。限速120KM/H而你以160KM/H的速度在开。很不幸,一个交警发现你超速了,让你停到路边。现在你进入了WAITING状态。你听下车坐在那等着交警过来检查你并放行。基本上,只有等他让你走。而这时你被卡在WAITING状态了。

用技术术语来讲,你是线程T1而交警是线程T2。你释放你的锁(例子中你停下了车),并进入WAITING状态,直到警察(例子中T2)让你走,你陷入了WAITING状态。

小贴士:当线程调用以下方法时会进入WAITING状态:

  • Object#wait() 而且不加超时参数

  • Thread#join() 而且不加超时参数

  • LockSupport#park()

在对象上的线程调用了Object.wait()会进入WAITING状态,直到另一个线程在这个对象上调用了Object.notify()或Object.notifyAll()方法才能恢复。一个调用了Thread.join()的线程会进入WAITING状态直到一个特定的线程来结束。

2.3 TIMED_WAITING(超时等待)

Java文档官方定义TIMED_WAITING状态为:“一个线程在一个特定的等待时间内等待另一个线程完成一个动作会在这个状态”

真实生活例子:尽管充满戏剧性,你在面试中做的非常好,惊艳了所有人并获得了高薪工作。(祝贺你!)你回家告诉你的邻居你的新工作并表达你激动的心情。你的朋友告诉你他也在同一个办公楼里工作。他建议你坐他的车去上班。你想这不错。所以第一天,你走到他的房子。在他的房子前停好你的车。你等了10分钟,但你的邻居没有出现。你继续开自己的车去上班,这样你不会在第一天就迟到。这就是TIMED_WAITING.

用技术术语来解释,你是线程T1而你的邻居是线程T2。你释放了锁(这里是停止开车)并等了足足10分钟。如果你的邻居T2没有来,你继续开车。

小贴士:调用了以下方法的线程会进入TIMED_WAITING:

  • Thread#sleep()

  • Object#wait() 并加了超时参数

  • Thread#join() 并加了超时参数

  • LockSupport#parkNanos()

  • LockSupport#parkUntil()

wait/sleep 的区别

  • 来自不同的类

    1. sleep Thread 的静态方法

    wait 是 Object 的方法,任何对象实例都 能调用

  • 关于锁的释放

    sleep 不会释放锁,它也不需要占用锁 wait 会释放锁,但调用它的前提 是当前线程占有锁(即代码要在 synchronized 中)

  • 使用的范围是不同的

wait 必须在同步代码块中 sleep 可以在任何地方使用

  • 是否需要捕获异常

    wait 不需要捕获异常 sleep 必须要捕获异常

并发与并⾏

1. 串行模式

串行表示所有任务都一一按先后顺序进行。串行意味着必须先装完一车柴才能运送这车柴,只有运送到了,才能卸下这车柴,并且只有完成了这整个三个步骤,才能进行下一个步骤。 串行是一次只能取得一个任务,并执行这个任务。

串行是一次只能取得一个任务,并执行这个任务。

2. 并行模式

并行意味着可以同时取得多个任务,并同时去执行所取得的这些任务。并行模式相当于将长长的一条队列,划分成了多条短队列,所以并行缩短了任务队列的长度。并行的效率从代码层次上强依赖于多进程/多线程代码,从硬件角度上 则依赖于多核 CPU。

3. 并发

并发(concurrent)指的是多个程序可以同时运行的现象,更细化的是多进程可 以同时运行或者多指令可以同时运行。但这不是重点,在描述并发的时候也不会去扣这种字眼是否精确,==并发的重点在于它是一种现象==, ==并发描述 的是多进程同时运行的现象==。但实际上,对于单核心 CPU 来说,同一时刻 只能运行一个线程。所以,这里的”同时运行”表示的不是真的同一时刻有多个 线程运行的现象,这是并行的概念,而是提供一种功能让用户看来多个程序同 时运行起来了,但实际上这些程序中的进程不是一直霸占 CPU 的,而是执行一 会停一会。

要解决大并发问题,通常是将大任务分解成多个小任务, 由于操作系统对进程的调度是随机的,所以切分成多个小任务后,可能会从任一小任务处执行。这可能会出现一些现象: • 可能出现一个小任务执行了多次,还没开始下个任务的情况。这时一般会采用队列或类似的数据结构来存放各个小任务的成果 • 可能出现还没准备好第一步就执行第二步的可能。这时,一般采用多路复用或异步的方式,比如只有准备好产生了事件通知才执行某个任务。 • 可以多进程/多线程的方式并行执行这些小任务。也可以单进程/单线程执行这 些小任务,这时很可能要配合多路复用才能达到较高的效率

4. 小结

并发:同一时刻多个线程在访问同一个资源,多个线程对一个点 例子:小米11今天上午10点,限量抢购 春运抢票 电商秒杀…

并行:多项工作一起执行,之后再汇总 例子:泡方便面,电水壶烧水,一边撕调料倒入桶中

管程

管程(monitor)是保证了同一时刻只有一个进程在管程内活动,即管程内定义的操作在同 一时刻只被一个进程调用(由编译器实现).但是这样并不能保证进程以设计的顺序执行 JVM 中同步是基于进入和退出管程(monitor)对象实现的,每个对象都会有一个管程 (monitor)对象,管程(monitor)会随着 java 对象一同创建和销毁 执行线程首先要持有管程对象,然后才能执行方法,当方法完成之后会释放管程,方法在执行时候会持有管程,其他线程无法再获取同一个管程

用户线程和守护线程

用户线程:平时用到的普通线程,自定义线程 守护线程:运行在后台,是一种特殊的线程,比如垃圾回收 当主线程结束后,用户线程还在运行,JVM 存活 如果没有用户线程,都是守护线程,JVM 结束

java获取cpu核心线程数

  1. // 获取cpu核心线程数
  2. // CPU 密集型,IO密集型
  3. System.out.println(Runtime.getRuntime().availableProcessors()); // 12