JUC概述
JUC:java.util.concurrent,是一个处理Java线程的工具包
知识回顾
进程和线程的概念
进程是计算机中的程序关于某数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位,是操作系统结构的基础。 在早期面向进程设计的计算机结构中,进程是程序的基本执行实体;在当代面向线程设计的计算机结构中,进程是线程的容器。 程序是指令、数据及其组织形式的描述,进程是程序的实体。
以上是百度百科专业解答,简而言之就是:
进程就是应用程序在内存中分配的空间,也就是正在运行的程序,各个进程之间互不干扰。同时进程保存着程序每一个时刻运行的状态,进程就是资源分配的最小单位。
程序:用某种编程语言(java、python等)编写,能够完成一定任务或者功能的代码集合,是指令和数据的有序集合,是一段静态代码。
此时,CPU采用时间片轮转的方式运行进程:CPU为每个进程分配一个时间段,称作它的时间片。如果在时间片结束时进程还在运行,则暂停这个进程的运行,并且CPU分配给另一个进程(这个过程叫做上下文切换)。如果进程在时间片结束前阻塞或结束,则CPU立即进行切换,不用等待时间片用完。
当进程暂停时,它会保存当前进程的状态(进程标识,进程使用的资源等),在下一次切换回来时根据之前保存的状态进行恢复,接着继续执行。
线程是操作系统向其分配处理器时间的基本单元。 每个线程具有计划优先级并维护系统用于保存线程执行暂停时线程上下文的一组结构。 线程上下文包含线程顺畅继续执行所需的全部信息,包括线程的一组CPU 寄存器和堆栈。 多个线程可在进程上下文中运行。
用人话来说就是为了让进程的子任务同时执行,人们又提出了线程的概念,让一个线程执行一个子任务,这样一个进程就包含了多个线程,每个线程负责一个单独的子任务。
总结一下:进程让操作系统的并发性成为了可能,而线程让进程的内部并发成为了可能。
思考:多进程的方式也可以实现并发,为什么我们要使用多线程?
多进程方式确实可以实现并发,但使用多线程,有以下几个好处:
- 进程间的通信比较复杂,而线程间的通信比较简单,通常情况下,我们需要使用共享资源,这些资源在线程间的通信比较容易。
- 进程是重量级的,而线程是轻量级的,故多线程方式的系统开销更小。
进程是一个独立的运行环境,而线程是在进程中执行的一个任务。他们两个本质的区别是是否单独占有内存地址空间及其它系统资源(比如I/O)
-
进程单独占有一定的内存地址空间,所以进程间存在内存隔离,数据是分开的,数据共享复杂但是同步简单,各个进程之间互不干扰;而线程共享所属进程占有的内存地址空间和资源,数据共享简单,但是同步复杂。
疑问:数据同步指的是什么?
-
进程单独占有一定的内存地址空间,一个进程出现问题不会影响其他进程,不影响主程序的稳定性,可靠性高;一个线程崩溃可能影响整个程序的稳定性,可靠性较低。
-
进程单独占有一定的内存地址空间,进程的创建和销毁不仅需要保存寄存器和栈信息,还需要资源的分配回收以及页调度,开销较大;线程只需要保存寄存器和栈信息,开销较小。
另外一个重要区别是,进程是操作系统进行资源分配的基本单位,而线程是操作系统进行调度的基本单位,即CPU分配时间的单位 。
上下文切换
指的是进程切换或者说是任务切换,就是cpu从一个进程(或线程)切换到另一个进程(或线程)。上下文是某一时间点cpu寄存器和程序计数器的内容。
- 寄存器:就是cpu里边数量少但是速度很快的闪存,存储和访问计算过程中常用的一些数值来提高运行速度,大概意思就是你既然经常要用到就别走了,就在内存待着省得每次都要进行读取浪费时间。
- 计数器:存的是cpu正在执行的位置,存的值为正在执行的指令位置或者下一个将要被执行的指令位置。
举例说明 线程A - B
1.先挂起线程A,将其在cpu中的状态保存在内存中。
2.在内存中检索下一个线程B的上下文并将其在 CPU 的寄存器中恢复,执行B线程。
3.当B执行完,根据程序计数器中指向的位置恢复线程A。
线程的几种状态
-
初始(NEW):新创建了一个线程对象,但还没有调用start()方法。
-
运行(RUNNABLE):Java线程中将就绪(ready)和运行中(running)两种状态笼统的称为“运行”。
线程对象创建后,其他线程(比如main线程)调用了该对象的start()方法。该状态的线程位于可运行线程池中,等待被线程调度选中,获取CPU的使用权,此时处于就绪状态(ready)。就绪状态的线程在获得CPU时间片后变为运行中状态(running)。 -
阻塞(BLOCKED):表示线程阻塞于锁。
-
等待(WAITING):进入该状态的线程需要等待其他线程做出一些特定动作(通知或中断)。
-
超时等待(TIMED_WAITING):该状态不同于WAITING,它可以在指定的时间后自行返回。
-
终止(TERMINATED):表示该线程已经执行完毕。
wait和sleep的区别
- sleep 是Thread的静态方法,wait是object的方法,任何对象实例都能调用。
- sleep不会释放锁,也不会占用锁,wait会释放锁,但调用它的前提是当前线程占有锁(即代码要在synchronized中)。
- 都能被interrupted方法中断。
并发和并行
-
并发:多个线程同时访问一个资源,多个线程对应一个点
- 如春节抢票、电商秒杀、市场抢菜
-
并行:多个线程同时执行,最后再汇总
- 泡方便面,烧水的同时可以做其他工作互不影响,充分节省时间。
管程
管程实际上就是一种Monitor(监视器,操作系统中的说法),也就是Java常说的锁。
是一种同步机制,也就是说保证同一时间,只有一个线程能访问被保护数据或者代码。
JVM中的同步是基于进入和退出实现的,使用管程对象实现的,进入加锁,退出解锁。
用户线程和守护线程
- 用户线程:自定义线程
- 守护线程:后台默默执行的线程(比如垃圾回收)
public static void main(String[] args)
{
Thread aa = new Thread(()->{
System.out.println(Thread.currentThread().getName()+"::"+Thread.currentThread.isDaemon());
while(true){
//死循环表示一个用户线程不断地在执行
}
},"aa");
aa.start();
System.out.println(Thread.currentThread().getName()+"over");
}
//打印结果
main over
aa::false
结果分析:主线程结束如果还存在用户线程,那么JVM还不会结束。
public static void main(String[] args)
{
Thread aa = new Thread(()->{
System.out.println(Thread.currentThread().getName()+"::"+Thread.currentThread.isDaemon());
while(true){
//死循环表示一个用户线程不断地在执行
}
},"aa");
aa.setDaemon(true);//设置为守护线程
aa.start();
System.out.println(Thread.currentThread().getName()+"over");
}
//打印结果
main over
结果分析:如果没有了用户线程只有守护线程,那么JVM会直接结束。
--相关基础概念&spm=1001.2101.3001.11974&articleId=124899043&d=1&t=3&u=0421616abaca4f4486c5d9e700d5d61a)
1505


--相关基础概念&spm=1001.2101.3001.11976&articleId=124899043&d=1&t=3&u=9746273df5d9474d8d929865b90393be)

被折叠的 条评论
为什么被折叠?



