重拾 Java 高并发编程:线程

78 阅读2分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 1 天,点击查看活动详情

前言

虽然我工作中主要是编写 Go 语言,但是对于 Java 高并发相关的知识点,我还是时不时会温故下。今天,就一起来从 Java 的线程开始,对高并发编程有更加深刻的认识吧。

什么是线程?

在操作系统中,线程是能够独立运行的基本单位,也是CPU调度的基本单位。线程拥有一些在运行时需要用到的系统资源,例如程序计数器,寄存器和栈等。一个进程中会包含多个线程,而所有线程可以共享进程中的所有资源。

在 Java 中,实现线程的方式分为三种

  • 继承Thread类
  • 实现Runnable接口
  • 实现Callable接口

我们一起看看代码。

继承 Thread 类

这种实现方式在我们日常开发中使用是比较常见的,简单来说就是定义一个继承 Thread 的类,并重写其 run() 方法:

public class ThreadLearning extends Thread {
    @Override
    public void run() {
        // 业务逻辑
    }
}

实现 Runnable 接口

这种实现方式在我们日常开发中也经常用到,它需要实现 Runnable 接口中的 run() 方法,跟继承 Thread 类差别不大:

public class RunnableLearning implements Runnable{

    @Override
    public void run() {
        // 业务逻辑
    }
}

实现 Callable 接口

上述两种实现方式有一定的局限性,我们看到 run() 方法的返回值都是 void,如果我们需要获取到线程执行时的返回值,那么我们就可以用实现 Callable 接口的方式来创建一个线程,并实现其中的 call() 方法:

public class CallableLearning implements Callable<String> {

    @Override
    public String call() throws Exception {
        // 业务逻辑
        return "业务逻辑返回值";
    }
}

Callable 是一个泛型接口,线程的返回值类型是什么,那么 call() 方法的返回值就是什么。

线程的生命周期

一个线程的命周期需要经历多种不同的状态,从网上找到一张图,很好地概括了线程从创建到消亡的各个状态之间的变换:

image.png

如图所示,线程的各个状态为:

  • New:线程刚刚被初始化,但是还未调用 Tread.start() 方法

  • Runnable:线程调用了 start() 方法,进入可运行状态:

    • Running:操作系统给线程分配了资源,开始调度线程,线程进入运行状态
    • Ready:操作系统执行 yeild(),线程又由运行状态变为就绪状态
  • Blocked:线程未获取到锁,进入阻塞状态,直到其他线程释放锁

  • Waiting:等待状态,线程需要等待其他线程通知

  • Time Waiting:超时等待状态,线程等到一定的超时时间就会自行进入其他状态

  • Terminated:线程执行完毕,进入终止状态。