概念梳理

并发与并行

  • 并发

在操作系统中,是指一个时间段中有几个程序都处于已启动运行到运行完毕之间,且这几个程序都是在同一个处理机上运行。

  • 并行

当系统有一个以上CPU时,当一个CPU执行一个进程时,另一个CPU可以执行另一个进程,两个进程互不抢占CPU资源,可以同时进行,这种方式我们称之为并行(Parallel)。

进程 线程 纤程

  • 进程

    进程是计算机中已运行程序的实体。

  • 线程

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

以上内容取自维基百科,从上面的描述来看,进程实质上就是一个程序运行起来的状态;线程就是一个进程中不同的执行路径。从更专业的角度上来看,进程实质上操作系统分配资源的基本单位;而协程是执行调度的基本单位。(分配资源就好比当你打开一个程序,操作系统在内存上为你划分一块区域来加载你的程序)。每个进程之间的内存空间是独立的,而各个进程内的线程是共享内存空间的,没有自己的独立内存空间。这是进程和线程比较重大的区别。

  • 纤程

那什么是纤程呢?纤程其实我们可以理解为线程的线程。系统执行调度的时候是使用线程作为基本单位,是不可控的,但是我们又需要一定的顺序执行,这时候就需要使用锁来解决。那纤程的调度,完全是由用户可以控制的。纤程还可以理解为用户态的线程,他不会涉及到用户态与内核态的切换。因为不需要经过操作系统的切换和调度,那么他速度肯定也是最快的。纤程相比线程来说,占用的资源也更少。一个线程所占资源大概在4k左右,而一个线程则是1M。

目前支持内置纤程的语言:Go Scala Kotlin Python(需要三方库)

线程的状态

线程共分为6个状态,分别是以下:

  • NEW(创建)
  • RUNNABLE(可运行)
    • READY(就绪)
    • RUNNING(正在运行)
  • BLOCK(阻塞)
  • WAITING(无限期等待)
  • TIME WAITING(有限期等待)
  • TERMINATE(终止)

下图描述了线程状态的切换
image.png

阻塞与等待的区别就在于阻塞是被动的,该线程正在等待被其他线程先占用的monitor lock,所以处于阻塞;而等待则是主动去调用方法进入等待状态。

线程的使用

创建线程

创建线程,那么如何创建一个线程呢?Java中创建线程有3种方式。

继承Thread类

  1. public class MyThread extends Thread{
  2. public void run() {
  3. // ...
  4. }
  5. }

实现Runnable接口

  1. public class MyRunnable implements Runnable {
  2. @Override
  3. public void run() {
  4. // ...
  5. }
  6. }

线程池

  1. public static void main(String[] args) {
  2. Executors.newCachedThreadPool(); //创建一个线程池,该线程池根据需要创建新线程,但是将在先前构造的线程可用时重用它们。
  3. Executors.newFixedThreadPool(10);//创建一个线程池,该线程池重用在共享的无边界队列上运行的固定数量的线程。
  4. Executors.newSingleThreadExecutor();//创建单个线程的线程池,相当于newFixedThreadPool(1);
  5. Executors.newScheduledThreadPool(2);//创建一个线程池,该线程池可以安排命令在给定的延迟后运行或定期执行。
  6. Executors.newSingleThreadScheduledExecutor();//创建一个单线程执行器,该执行器可以计划命令在给定的延迟后运行或定期执行。
  7. }

线程方法

sleep

sleep表示睡眠,那么sleep的意思就是当前线程暂停一段时间让给别的线程去执行,具体多少时间取决于你参数设置。

  1. @Override
  2. public void run() {
  3. try {
  4. Thread.sleep(200);
  5. } catch (InterruptedException e) {
  6. e.printStackTrace();
  7. }
  8. }

yield

yield有个释义表示放弃,那么yield表示的意思就是当前线程正在执行的时候放弃继续执行,回到等待队列中,等待系统下一次的调度。

  1. @Override
  2. public void run() {
  3. Thread.yield();
  4. }

join

join表示加入,那么join表示的意思就是当前线程调用另外一个线程的join方法时,会将当前线程挂起,让另外一个线程执行完毕之后,当前线程继续执行。

  1. Thread t1 = new Thread(() -> {
  2. for (int i = 0; i < 5; i++) {
  3. System.out.println("t1:" + i);
  4. try {
  5. TimeUnit.SECONDS.sleep(1);
  6. } catch (InterruptedException e) {
  7. e.printStackTrace();
  8. }
  9. }
  10. });
  11. Thread t2 = new Thread(() -> {
  12. try {
  13. t1.join();
  14. } catch (InterruptedException e) {
  15. e.printStackTrace();
  16. }
  17. for(int i = 0; i < 5; i++) {
  18. System.out.println("t2:" + i);
  19. try {
  20. TimeUnit.SECONDS.sleep(1);
  21. } catch (InterruptedException e) {
  22. e.printStackTrace();
  23. }
  24. }
  25. });
  26. t1.start();
  27. t2.start();

参考链接

进程 线程 协程
CS-Notes/Java 并发
Java工程师成神之路