通过Thread类和Runnable接口来了解Java的设计

1.说在前面

在面试的时候经常会问Thread类和Runnable接口的区别是什么,在我看来它俩之间没啥关系,Thread类是对操作系统线程的封装,Runnable接口是Java对线程任务的定义,要知道,线程包含了线程的运行环境和任务逻辑,Runnable接口不过是Java语言对线程任务的定义。

学任何一门编程语言如果只了解怎样使用是远远不够的,需要深入了解其设计思想。Java是一门完全面向对象的语言,Java的类库设计使用了非常多的设计模式,也基于操作系统提供的基础能力通过封装衍生出很多新的能力,只有把这些搞清楚才算是真正学懂一门编程语言,而且一通百通,在学习其他编程语言的时候很多思想都可以迁移过去,让学习事半功倍。

2.进程和线程的区别

可以用两句话来说明进程和线程之间的区别,两句话要表达的思想是一致的,但是角度稍有不同:

第一句:进程是操作系统分配资源的最小单位,线程是操作系统调度执行的最小单位

操作系统负责分配计算机的各种资源,包括CPU时间、内存、文件句柄、网络连接等,每个进程都需要这些资源来执行任务,而操作系统分配这些资源的时候是以进程为基本单位来分配的。这意味着操作系统以进程为单位来决定哪个程序可以运行、分配多少内存给程序、以及如何管理程序之间的通信。因此,进程是操作系统进行资源分配和管理的最小单元。

在操作系统中,线程是基本的执行单元,它们是操作系统进行调度和管理的最小单元,操作系统的主要任务之一是将CPU时间分配给不同的线程,以便它们能够并行执行或交替执行。操作系统使用调度算法来确定哪个线程在某一时刻获得CPU时间片,以便执行其任务。

综上,也就是说,操作系统在分配系统资源的时候是以进程为单位来分配的;在调度执行的时候,是以线程为单位来调度执行的。

第二句:进程的本质是操作系统资源的载体,线程的本质是一个执行流

这意味着线程是程序中可以独立执行的任务单元,它们具有自己的执行环境和可以执行的指令序列。这使得线程可以同时执行不同的任务,而不会相互干扰。

3.为什么要把线程的实现分成了Thread类和Runnable接口

在Java里面把一个线程的实现分成了两个部分:Thread类和Runnable接口,为什么要做这样的设计呢?背后体现了怎样的设计思想呢?

要讲清楚这个问题,就要从操作系统是怎样实现一个线程的说起。每个线程都有堆栈和寄存器,这些都是线程的运行环境,每个线程都有一套自己的运行环境;同时,除了运行环境,每个线程还有自己要执行的任务,也就是代码序列,不同的线程执行的代码序列可能是不同的,但线程的运行环境是相同的。因此,一个线程可以抽象为线程的运行环境 + 线程执行的任务 ,Thread类的实现就类似线程的运行环境,而Runnable接口就类似线程执行的任务,这是一种解耦的设计思想,即,把线程的运行环境和线程执行的任务进行分离。

这种设计方式使得程序设计非常灵活,比如要实现这样一个场景:

有两个任务要完成:

任务一:做一个柿子炒鸡蛋(搅拌鸡蛋 -> 起锅烧油 -> 炒鸡蛋 -> 装盘)

任务二:去商场买衣服(开车到商场 -> 试衣服 -> 前台买单)

单线程实现方式:两个任务在一个执行流里面,先做任务一,再做任务二,实现如下:

csharp 复制代码
***********TaskOne**************
public class TaskOne {
    public void run() {
        System.out.println("任务一:1.搅拌鸡蛋");
        System.out.println("任务一:2.起锅烧油");
        System.out.println("任务一:3.炒鸡蛋");
        System.out.println("任务一:4.装盘");
    }
}
************TaskTwo**************
public class TaskTwo {
    public void run() {
        System.out.println("任务二:1.开车到商场");
        System.out.println("任务二:2.试衣服");
        System.out.println("任务三:3.前台买单");
    }
}
************ThreadTest***********
public class ThreadTest extends Thread {
    @Override
    public void run() {
        new TaskOne().run();
        new TaskTwo().run();
    }

    public static void main(String[] args) {
        new ThreadTest().start();
    }
}

多线程实现方式:两个任务分别用一个线程(执行流),任务一和任务二同时做,实现如下:

csharp 复制代码
************TaskOne************
public class TaskOne implements Runnable {
    public void run() {
        System.out.println("任务一:1.搅拌鸡蛋");
        System.out.println("任务一:2.起锅烧油");
        System.out.println("任务一:3.炒鸡蛋");
        System.out.println("任务一:4.装盘");
    }
}
************TaskTwo************
public class TaskTwo implements Runnable {
    public void run() {
        System.out.println("任务二:1.开车到商场");
        System.out.println("任务二:2.试衣服");
        System.out.println("任务三:3.前台买单");
    }
}
************ThreadTest************
public class ThreadTest{
    public static void main(String[] args) {
        TaskOne t1 = new TaskOne();
        TaskTwo t2 = new TaskTwo();
        new Thread(t1).start();
        new Thread(t2).start();
    }
}

从这两个demo中可以看出,如果希望任务一和任务二在同一个线程中完成,只需要创建一个线程,然后把任务一和任务二的代码逻辑直接放到线程内执行。如果想要让任务一和任务二分别在不同的线程中运行,只需要做很简单的改造:1、让每个任务实现Runnable接口,把单纯的逻辑代码变成线程的任务;2、通过Thread类创建一个线程运行环境,然后把任务加进去。这样简单的改造,就把两个任务变成了两个线程。

相关推荐
凭君语未可21 分钟前
豆包MarsCode:小C的类二进制拼图
java·算法
master-dragon2 小时前
Java锁自定义实现到aqs的理解
java·开发语言
hamster20212 小时前
力扣【1049. 最后一块石头的重量 II】Java题解(背包问题)
java·算法·leetcode
Cikiss3 小时前
「全网最细 + 实战源码案例」设计模式——桥接模式
java·后端·设计模式·桥接模式
Dr_Si3 小时前
CF 761A.Dasha and Stairs(Java实现)
java·开发语言
你爱写程序吗(新H)3 小时前
基于微信小程序的停车场管理系统设计 停车场微信小程序的设计与实现 (源码+文档)
java·spring boot·微信小程序·小程序
讓丄帝愛伱4 小时前
Java 泛型<? extends Object>
java·开发语言·python
Dr_Si4 小时前
CF 764B.Timofey and cubes(Java实现)
java·算法·排序算法
纪元A梦4 小时前
Java设计模式:行为型模式→观察者模式
java·观察者模式·设计模式
大秦王多鱼5 小时前
Kafka常见问题之 java.io.IOException: Disk error when trying to write to log
java·运维·分布式·kafka