开篇
本文内容是综合各种资料(博客+视频+书籍)整理而来。具体链接没有记录,望见谅!
正文
在学习多线程知识时,除了线程定义及使用方法之外,我们经常会了解到以下概念:
- 进程与线程
- 时间片
- CPU 执行权
- 上线文切换
- 并发 和 并行
- 守护线程
- 等等
如果没有计算机基础,上面很多概念一时半会儿无法理解的。下面就针对这些知识点来一一说明下。
进程与线程的关系
举个例子,当我们启动 Java 中的 main 函数时,就等于启动了一个 JVM 进程,而 main 函数所在的线程就是 JVM 进程中一个线程(我们经常称之为主线程)。
public class App {
public static void main( String[] args ){
while (true){
// doSomething
}
}
}
# jstack 打印出的堆栈信息
"main" #1 prio=5 os_prio=31 tid=0x00007ff38c801800
nid=0x2503 runnable [0x00007000076f6000]
java.lang.Thread.State: RUNNABLE
at com.nimo.App.main(App.java:9)
当我们在 main 函数中,再次启动一个线程时:
public class App {
public static void main( String[] args ){
while (true){
new Thread(()->{
//doSomething
}).start();
}
}
}
此时 JVM 进程中至少有两个线程,一个是主线程,一个是刚刚启动的线程。这就说明一个进程中至少可以有一个线程。
上下文切换
一个 CPU 同一时刻只能被一个线程占用,尽管现在都是多核 CPU, 但是一般情况下,线程的数量还是会大于 CPU 数量。为了让我们感觉多个线程是在同一时刻同时执行的,CPU 采用了时间片轮转的资源分配策略:给每个线程分配 一个时间片,线程在时间片内可以拥有 CPU 执行权;线程使用完时间片后,就会处于就绪状态,并把 CPU 执行权让给其他线程, 这就是所谓的上下文切换。
守护线程
跟守护线程(daemon)相对应的是用户线程,比如 main 函数所在的线程就是一个用户线程, Jvm 中的垃圾回收线程就是一个守护线程,这也是上面为什么说,一个 Java 进程中为什么至少会有一个线程的原因。
Java 中通过调用 thread 对象的 setDaemon(true) 方法来定义一个守护线程
public class App {
public static void main( String[] args ){
while (true){
Thread t = new Thread(()->{
// doSomething
});
t.setDaemon(true);
t.start();
}
}
}
用户线程和守护线程的区别
当所有的用户线程都执行结束后,JVM 才会退出。换句话说,JVM进程是否退出不会受守护线程的影响。
对比以下两个程序:
// 方式一: 通过线程池创建线程
public class App {
public static void main( String[] args ){
ExecutorService executorService = Executors.newFixedThreadPool(1);
executorService.submit(()->{
System.out.println("hello world");
});
}
}
// 方式二: 通过 new Thread 创建线程
public class App {
public static void main( String[] args ){
Thread t = new Thread(()->{
System.out.println("hello world");
});
t.start();
}
}
通过两种方式创建的线程,他们的执行结果是一样的,都是打印一句 ‘Hello world’, 但是执行效果不一样,方式一的 JVM 进程永远不会退出。因为线程池创建用户线程还存在。
并发和并行
并发是指 同一个时间段内 多个任务同时都在执行,并且都没有执行结束,而并行是说在 单位时间内 多个任务同时在执行 。
一个时间段是由多个单位时间组成的, 因此在单位时间内,多个任务不一定同时执行。
换句话说,在单个 CPU 的情况下,多个任务都是在并发执行。
线程模型
Java 中的线程和操作系统内核态的线程是一对一的关系,因此原则上一个线程阻塞,不会影响其他线程的运行,但是操作系统不会允许用户无限制的创建线程。
线程的执行状态
当调用线程的 start 方法时,线程只是拿到了除 CPU 资源以外的其他所有资源,因此,此时线程还未开始执行。只是处于就绪状态。
当获取到 CPU 资源后,线程才会真正处于运行状态。
一旦 run 方法执行完毕,该线程就处于终止状态。
总结
要想对线程有足够深入的理解,还是要有计算机相关的基础。不然一些概念只能随着时间慢慢生啃。在此,推荐阮一峰大神的文章 进程与线程的一个简单解释
来源:oschina
链接:https://my.oschina.net/u/3984985/blog/3178279