Java 多线程核心知识点整理

时光总嘲笑我的痴心妄想 提交于 2020-02-28 17:06:33

开篇

本文内容是综合各种资料(博客+视频+书籍)整理而来。具体链接没有记录,望见谅!

正文

在学习多线程知识时,除了线程定义及使用方法之外,我们经常会了解到以下概念:

  • 进程与线程
  • 时间片
  • 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 方法执行完毕,该线程就处于终止状态。

总结

要想对线程有足够深入的理解,还是要有计算机相关的基础。不然一些概念只能随着时间慢慢生啃。在此,推荐阮一峰大神的文章 进程与线程的一个简单解释

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!