在Java中,线程部分是一个重点,本篇文章说的是JUC也是关于线程的。JUC就是java.util.concurrent工具类的简称。只是一个处理线程的工具类,JDK1.5开始出现的。
进程:
是计算机中的程序关于某数据集合上的一次运行活动,是系统进行资源分匹配和调度的基本单位,是操作系统结构的基础。在当代面向线程设计的计算机结构,进程是线程的容器。程序是指令,数据及其组织形式的描述,进程是程序的实体。
线程:
是操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运行单位,一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并执行不同的任务。
从图可以看出:一个进程中可以有多个线程,多个线程共享进程的堆和方法区(JDK 1.8之后的原空间)资源,但是每个线程有自己的程序计数器,虚拟机栈,本地方法栈
总结:线程是进程划分的更小的运行单位。线程和进程最大的不同在于基本上各进程是独立的,而线程不一定独立,因为同一进程中的线程共用同一个进程的上下文,极有可能会相互影响。线程执行开销小,但不利于资源的保护和管理;而进程则相反。
串行模式
一次只能取的一个任务,并执行这个任务。
并行模式
同时取得多个任务,并同时去执行任务,并行缩短了任务队列的长度。并行的效率从代码层次上强依赖于多线程/多线程代码,从硬件上则依赖于多核CPU。
并发:
并发指的是多个程序可以同时运行的现象,更细化的是多线程可以同时运行或者多指令可以同时运行。
同一时刻多个线程在访问统一资源,多个线程对一个点
并行:
多项工作一起执行,之后再汇总。
总结:
并发:两个及两个以上的作业在同一 时间段内执行。
并行:两个及两个以上的作业在同一 时刻 执行。
管程是保证同一时刻只有一个进程在管程内活动,即管程内定义的操作在同一时刻只被一个进程调用(由编译器实现),但是这样并不能保证进程以设计的顺序执行。
JVM 中同步是基于进入和退出管程(monitor)对象实现的,每个对象都会有一个管程 (monitor)对象,管程(monitor)会随着 java 对象一同创建和销毁
执行线程首先要持有管程对象,然后才能执行方法,当方法完成之后会释放管程,方法在执行时候会持有管程,其他线程无法再获取同一个管程
用户线程:平时用到的普通线程,自定义线程
守护线程:运行在后台,是一种特殊的线程,比如垃圾回收
当主线程结束后,用户线程还在运行,JVM存活
如果没有用户线程都是守护线程JVM结束
Java线程在运行的生命周期中的指定时刻只会处于下面6中不同的状态
线程在生命周期中并不是固定处于某种状态,而是随着代码的执行在不同的状态之间切换,
由上图可以看出:线程在创建之后它将会处于NEW(新建)状态,调用start()方法后开始运行,线程这时候处于READY(可运行)状态,可运行状态的线程获得了CPU时间片后就会转为RUNNING(运行)状态,
在操作系统中层面线程有 READY 和 RUNNING 状态,而在 JVM 层面只能看到 RUNNABLE 状态,所以 Java 系统一般将这两个状态统称为 RUNNABLE(运行中) 状态 。
为什么 JVM 没有区分这两种状态呢? 现在的时分(time-sharing)多任务(multi-task)操作系统架构通常都是用所谓的“时间分片(time quantum or time slice)”方式进行抢占式(preemptive)轮转调度(round-robin式)。这个时间分片通常是很小的,一个线程一次最多只能在 CPU 上运行比如 10-20ms 的时间(此时处于 running 状态),也即大概只有 0.01 秒这一量级,时间片用后就要被切换下来放入调度队列的末尾等待再次调度。(也即回到 ready 状态)。线程切换的如此之快,区分这两种状态就没什么意义了
当线程执行 wait()
方法之后,线程进入 WAITING(等待) 状态。进入等待状态的线程需要依靠其他线程的通知才能够返回到运行状态,而 TIMED_WAITING(超时等待) 状态相当于在等待状态的基础上增加了超时限制,比如通过 sleep(long millis)
方法或 wait(long millis)
方法可以将 Java 线程置于 TIMED_WAITING 状态。当超时时间到达后 Java 线程将会返回到 RUNNABLE 状态。当线程调用同步方法时,在没有获取到锁的情况下,线程将会进入到 BLOCKED(阻塞) 状态。线程在执行 Runnable 的run()
方法之后将会进入到 TERMINATED(终止) 状态。
程序在执行过程中会有自己的运行条件和状态(也称上下文),如程序计数器,栈信息等,当出现如下情况,线程会从和占有CPU状态中退出
这其中前四种都会发生线程切换,线程切换意味着需要保存当前线程的上下文,留待线程下次占用 CPU 的时候恢复现场。并加载下一个将要占用 CPU 的线程上下文。这就是所谓的 上下文切换。
上下文切换是现代操作系统的基本功能,因其每次需要保存信息恢复信息,这将会占用 CPU,内存等系统资源进行处理,也就意味着效率会有一定损耗,如果频繁切换就会造成整体效率低下
这是一个经典的Java多线程面试题,会被经常问道!!!
new一个Thread,线程进入了新建状态(new),调用start()方法,会启动一个线程并使线程进入就绪状态,当分配到时间片之后就可以运行了(万事俱备,只欠CPU),start()会执行线程的相应准备工作,然后自动执行run()方法的内容,这是真正的多线程工作,但是,直接调用run()方法的内容,Java会把run()方法当成一个main线程下的普通方法执行,并不会在某个线程中执行它,所以这并不是多线程工作。
总结: 调用 start() 方法方可启动线程并使线程进入就绪状态,直接执行 run() 方法的话不会以多线程的方式执行。