Java 17 多线程 Thread 的基础知识点

Java
286
0
0
2023-09-17
标签   Java多线程

Java 17 多线程 Thread 的基础知识点

这节开始说说 Java 中的 多线程 ,在说多线程之前,先说说这些基础的概念。

说 线程 前,先说说进程。

“进程”这一术语在20世纪60年代初期首先于美国麻省理工学院的MULTICS系统和 IBM公司 的CTSS/360系统中引入。进程是操作系统中的一个最基本也是最重要的概念。

对于进程而言,一般具有 5 个特征:动态性、并发性、独立性、异步性、结构特征。

因为操作系统没有真正的并行,所以对于进程来说会有不同的运行状态,包含 3 种基础状态:就绪状态、执行状态、阻塞状态。也就是说频繁的切换不同的进程让你看起来像是多个进行同时进行。

线程的概念

自从 20 世纪 60 年代提出进程的概念后,进程就是在操作系统中作为能独立运行的基本单位。接着来到了 20 世纪 80 年代中期,当时的前辈们又提出了比进程更小的能独立运行的基本单位 “线程”。线程的提出为了提高系统中程序并发执行的程度,从而可以提供系统的吞吐量。

对于线程而言,是为了使程序能够并发执行。与之对应的,线程也同样也有就绪、阻塞和执行三种状态。

并且对于线程有不同的属性。

  • 都有一个唯一的标识符。
  • 不同的线程也可以执行相同的程序。
  • 同一个进程中的各个线程共享对应进程的内存地址空间。
  • 线程被创建后就有对应的 生命周期 ,直到终止,线程在生命周期内会经历阻塞状态、就绪状态和执行状态等各种状态变化。

以上都是些基础概念,更多详细的可以参考操作系统中的进程与线程章节,更加详细。

说了这么多现在开始来看看 Java 中线程是怎么操作的。

在 Java 中线程有 6 种状态:

  • New(新创建)
  • Runnable(可运行)
  • Blocked(被阻塞)
  • Waiting(等待)
  • Timed waiting(计时等待)
  • Terminated(被终止)

创建线程在 Java 中有三种方式,1. 继承 Thread 类创建线程;2. 实现 Runnable 接口的 run 方法创建线程;3. 使用 Callable 和 Future 创建线程。

首先先来看看使用 Thread 创建的方式。

Thread

类的定义:

 public class Thread implements Runnable 

Java 17 多线程 Thread 的基础知识点

根据线程的状态可以知道,线程想要使用必须先创建。如何进行创建呢。 看下该类的 构造函数 有哪些。

构造器

构造器


描述


Thread()


分配一个新的 Thread 对象。


Thread(Runnable target)


根据传入的 Runnable,分配一个新的 Thread 对象。


Thread(Runnable target, String name)


根据传入的 Runnable,和线程名称,分配一个新的 Thread 对象。


Thread(String name)


传入线程名称,分配一个新的 Thread 对象。


Thread(ThreadGroup group, Runnable target)


根据线程分组和Runnable,分配一个新的 Thread 对象。


Thread(ThreadGroup group, Runnable target, String name)


分配一个新的 Thread 对象,使其以 target 作为其运行对象,以指定的名称作为其名称,并属于 group 所引用的线程组。


Thread(ThreadGroup group, Runnable target, String name, long stackSize)


分配一个新的 Thread 对象,使其以 target 作为其运行对象,以指定的名称作为其名称,并属于由 group 引用的线程组,并具有指定的堆栈大小。


Thread(ThreadGroup group, Runnable target, String name, long stackSize, boolean inheritThreadLocals)


分配一个新的 Thread 对象,使其以 target 作为其运行对象,以指定的名称作为其名称,属于由 group 引用的线程组,具有指定的 stackSize,并继承可继承线程局部变量的初始值(如果 inheritThreadLocals)是真的。


Thread(ThreadGroup group, String name)


根据线程分组和线程字符名字,分配一个新的 Thread 对象。


为了更好的区分线程的关系,先看几个基础的方法,以及方法对应的含义。

线程的信息方法

修饰符和类型


方法


描述


long


getId()


返回此线程的标识符。


final String


getName()


返回此线程的名称。


final int


getPriority()


返回此线程的优先级。


Thread.State


getState()


返回此线程的状态。


final ThreadGroup


getThreadGroup()


返回该线程所属的线程组。


创建一个默认的对象。

 Thread thread = new Thread(); 

虽然能够创建对象,但是对于创建的默认构造, 并没有实际执行的实现,对于 Thread 是使用 start 启动一个线程,但是实际的线程实现却是 run 方法。

所以默认构造即使调用 start 方法, 也没有任何可以执行的方法体。但是也可以查看线程的一些基础的知识点。

线程的启动和实现

修饰符和类型


方法


描述


void


run()


如果该线程是使用单独的 Runnable 运行对象构造的,则调用该 Runnable 对象的 run 方法; 否则,此方法不执行任何操作并返回。


void


start()


使该线程开始执行; Java 虚拟机 调用该线程的 run 方法。


代码示例如下,显示线程的分组、线程的标识符、线程的名字以及线程的状态。

所以对于这种方式,可以直接使用子类实现 Thread 的 run 方法即可使用另一个线程启动应用。

举例说明:

 class MyThread  extends  Thread {
    @Override
    public void run() {
        for (int i =; i < 100; i++) {
            System.out.print(" T" + i);
        }
    }
}
public class Thread {
    public  static  void main(String[] args) {
        MyThread myThread = new MyThread();
        myThread.start();

        for (int i =; i < 100; i++) {
            System.out.print(" M" + i);;
        }
    }
} 

运行之后, 查看效果会发现。注意查看其中的效果。

查看 Thread(Runnable target) 构造函数。

 Thread thread = new Thread(()->{
    for (int i =; i < 100; i++) {
        System.out.print(" T" + i);
    }
});
thread.start();
for (int i =; i < 100; i++) {
    System.out.print(" M" + i);;
} 

注意上面的 ()->{} Lambda 表达式,等同于下面的代码:

 new Thread(new Runnable() {
    @Override
    public void run() {
        //待执行的代码
    }
}); 

对于分组,可以认为是为了标志一组线程。暂时不演示这个,先做简单的例子。

对于线程来说,有个 Sleep 静态方法 ,代表着当前线程需要等待多久时间继续运行。

线程的睡眠

修饰符和类型


方法


描述


static void


sleep(long millis)


使当前执行的线程休眠(暂时停止执行)指定的毫秒数,取决于系统计时器和调度程序的精度和准确性。


static void


sleep(long millis, int nanos)


使当前执行的线程休眠(暂时停止执行)指定的毫秒数加上指定的纳秒数,具体取决于系统计时器和调度程序的精度和准确性。


Java 17 多线程 Thread 的基础知识点

在 Thread 中有一个 join 方法,等待着线程的结束。

线程的等待

修饰符和类型


方法


描述


final void


join()


等待这个线程死掉。


final void


join(long millis)


最多等待几毫秒让该线程终止。


final void


join(long millis, int nanos)


最多等待毫秒加上纳秒以使该线程终止。


通常情况下, 如果一个子线程的运行时间比主线程时间长,这个时候,因为主线程的结束, 会关闭子线程。这个时候,可以使用 join 方法等待线程的结束,也可以直接默认的毫秒或者加上纳秒的时间结束线程。演示案例如下:

Java 17 多线程 Thread 的基础知识点

其中

 customThread.join(); 

修改成

 customThread.join(); 

重新执行查看效果:

Java 17 多线程 Thread 的基础知识点

这个时候, 你会发现,其中的差别,如果不指定时间的时候,将一直等待这该线程执行完毕,指定之后,就会继续执行。这个时候, 如果有流媒体之类的,已经发起执行, 会可能导致提前结束主线程。

线程如果已经执行如何进行中断, 以及如何判断中断的状态呢,在 API 中可以看到 stop 方法已经是过期的,并且该方法非安全性的,所以不在使用这个方法。

线程的中断

中断相关的方法有:

修饰符和类型


方法


描述


void


interrupt ()


中断这个线程。


boolean


isInterrupted()


测试此线程是否已被中断。


final boolean


isAlive()


测试此线程是否存活。


代码如下:

 public class Thread {
    public static void main(String[] args) throws Interrupted Exception  {
        CustomThread customThread = new CustomThread();
        customThread.start();
        Thread.sleep();
        customThread.interrupt();
        System.out.print("end main");
    }
}
class CustomThread extends Thread {
    @Override
    public void run() {
        System.out.println("CustomThread run");
        for (int i =; i < 100; i++) {
            try {
                if (this.isInterrupted()) {
                    break;
                }
                Thread.sleep();
            } catch (Exception e) {
                System.err.println("terr:" + e.getMessage());
                break;
            }
            System.out.print(" T" + i);
        }
    }
} 

代码运行的结果。

Java 17 多线程 Thread 的基础知识点

简单的说一下,需要注意的是,这里的中断只是做了中断的标志,具体要不要中断,以及中断之后怎么处理,还需要我们自己根据实际的业务进行判断。

生命周期演示

直接看演示的效果吧。 为了出现演示效果,另外又创建了一个线程。用来睡眠等待这第一个线程的结束。还有被阻塞和等待这两个生命周期状态不好演示,如果有办法更方便的演示,可以评论区留言,共同学习。

Java 17 多线程 Thread 的基础知识点

多线程的 Thread 基础使用先这样, 当然线程的知识还有很多, 另外会继续开篇再来说明。

感谢您的阅读,持续输出全栈知识,当前进度是 Java 的 0 到实战系列。欢迎关注收藏点赞。