我的编程空间,编程开发者的网络收藏夹
学习永远不晚

什么是多线程?进程和线程的区别是什么?如何使用Java实现多线程?

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

什么是多线程?进程和线程的区别是什么?如何使用Java实现多线程?

在这里插入图片描述

前言

前面我们了解了什么是进程以及如何实现进程调度,那么今天我将为大家分享关于线程相关的知识。在学习线程之前,我们认为进程是操作系统执行独立执行的单位,但其实并不然。线程是操作系统中能够独立执行的最小单元。只有掌握了什么是线程,我们才能实现后面的并发编程。

我们为什么要使用线程而不是进程来实现并发编程

实现并发编程为什么不使用多进程,而是使用多线程呢?主要体现在几个方面:

  • 创建一个进程的开销很大
  • 调度一个进程的开销很大
  • 销毁一个进程的开销很大

开销不仅体现在时间上,还体现在内存和 CPU 上。现在以”快“著称的互联网时代,这种大开销是不受人们欢迎的。那么为什么多线程就可以实现快捷的并发编程呢?

  • 共享资源:多个线程之间共用同一部分资源,大大减少了资源的浪费
  • 创建、调度、销毁的开销小:相较于进程的创建、调度和销毁,线程的创建、调度和销毁就显得很轻量,这样也大大节省了时间和资源的浪费
  • 现在的计算机 CPU 大多都是多核心模式,我们的多线程模式也更能利用这些优势

什么是线程

线程是操作系统能够独立调度和执行的最小执行单元。线程是进程内的一个执行流程,也可以看作是进程的子任务。与进程不同,线程在进程内部创建和管理,并且与同一进程中的其他线程共享相同的地址空间和系统资源。
只有当第一个线程创建的时候会有较大的开销,后面线程的创建开销就会小一点。并发编程会尽量保证每一个线程在不同的核心上单独执行,互不干扰,但也不可避免的出现在单核处理器系统中,线程在一个 CPU 核心上运行,它们通过时间片轮转调度算法使得多个线程轮流执行,给我们一种同时执行感觉。

线程是操作系统调度执行的基本单位

进程和线程的区别

一个进程中可以包含一个线程,也可以包含多个线程。

  1. 资源和隔离:进程是操作系统中的一个独立执行单位,具有独立的内存空间、文件描述符、打开的文件、网络连接等系统资源。每个进程都拥有自己的地址空间,进程间的数据不共享。而线程是进程内的执行流程,共享同一进程的地址空间和系统资源,可以直接访问和修改相同的数据。

  2. 创建和销毁开销:相对于进程,线程的创建和销毁开销较小。线程的创建通常只涉及创建一个新的执行上下文和一些少量的内存。而进程的创建需要分配独立的内存空间、加载可执行文件、建立进程控制块等操作,开销较大。

  3. 并发性和响应性:由于线程共享进程的地址空间,多个线程可以在同一进程内并发执行任务,共享数据和通信更加方便。因此,线程的切换成本较低,可以实现更高的并发性和响应性。而进程之间通常需要进程间通信(IPC)的机制来进行数据交换和共享,开销较大,响应性较低。

  4. 管理和调度:进程由操作系统负责管理和调度,每个进程之间是相互独立的。而线程是在进程内部创建和管理的,线程调度和切换由操作系统的线程调度器负责。线程的调度通常比进程的调度开销小,线程切换更快。

  5. 安全性和稳定性:由于进程之间相互独立,一个进程的崩溃不会影响其他进程的正常运行,因此进程具有更好的安全性和稳定性。而一个线程的错误或异常可能会导致整个进程崩溃。

前面我们所说的 PCB 其实也是针对线程来说的,一个线程具有一个 PCB 属性,一个进程可以含有一个或多个 PCB。

PCB 里的状态:上下文,优先级,记账信息,都是每个线程有自己的,各自记录自己的,但是同一个进程里的PCB之间,pid是一样的,内存指针和文件描述符表也是一样的。

如何使用Java实现多线程

在Java中使用一个线程大致分为以下几个步骤:

  1. 创建线程
  2. 启动线程
  3. 终止线程
  4. 线程等待

创建线程

在Java中执行线程操作依赖于 Thread 类。并且创建一个线程具有多种方法。

  1. 创建一个线程类继承自 Thread 类
  2. 实现 Runnable 接口

1.创建一个继承 Thread 类的线程类

class MyThread extends Thread {    @Override    public void run() {        System.out.println("这是一个MyThread线程");    }}

我们需要重写 run 方法,而 run 方法是指该线程要干什么。

创建实例对象

public class TreadDemo1 {    public static void main(String[] args) {        Thread t = new MyThread();    }}

2.实现 Runnable 接口

创建一个线程我们不仅可以直接创建一个继承自 Thread 的线程类,我们也可以直接实现 Runnable 接口,因为通过源码我们可以知道 Thread 类也实现了 Runnable 接口。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

我们可以将 Runnable 作为一个构造方法的参数传进去。

class MyRunnable implements Runnable {    @Override    public void run() {        System.out.println("这是一个线程");    }}public class ThreadDemo2 {    public static void main(String[] args) {        Thread t = new Thread(new MyRunnable());    }}

但是这种实现 Runnable 接口的方式会显得很麻烦,因为每个线程执行的内容大多是不同的,所以我们可以采用下面两种方式来实现 Runnable 接口。

  • 匿名内部类
  • lambda 表达式

匿名内部类方式实现 Runnable 接口

public class ThreadDemo3 {    public static void main(String[] args) {        Thread t = new Thread(new Runnable() {            @Override            public void run() {                System.out.println("这是一个线程");            }        });    }}

lambda 表达式实现 Runnable 接口

public class ThreadDemo4 {    public static void main(String[] args) {        Thread t = new Thread(() -> {            System.out.println("这是一个线程");        });    }}

Thread 类的常见构造方法

方法说明
Thread()创建对象
Thread(Runnable target)使用 Runnable 对象创建线程对象
Thread(String name)创建线程对象,并命名
Thread(Runnable target,String name)使用 Runnable 对象创建线程对象,并命名
Thread(ThreadGroup group,Runnable target线程可以被用来分组管理,分好的组即为线程组,这个我们目前了解即可

在这里插入图片描述

Thread 类有很多构造方法,大家有兴趣可以自己去看看。

Thread 的几个常见属性

属性获取方法
IDgetId()
名称getName()
状态getState()
优先级getPriority()
是否后台进程isDaemon()
是否存活isAlive
是否被中断isInterrupted()
  • ID 是线程的唯一标识,不同线程不会重复
  • 名称是各种调试工具用到
  • 状态表示线程当前所处的一个情况
  • 优先级高的线程理论上来说更容易被调度到
  • 关于后台线程,需要记住一点:JVM会在一个进程的所有非后台线程结束后,才会结束运行。
  • 是否存活,即简单的理解,为 run 方法是否运行结束了
  • 线程的中断问题,下面我们进一步说明

我们前面创建的都是前台进程,我们可以感知到的,那么什么叫做后台进程呢?

后台进程是指在计算机系统中以低优先级运行且不与用户交互的进程。与前台进程相比,后台进程在运行时不会占据用户界面或终端窗口,并且通常在后台默默地执行任务。

后台进程通常用于执行系统服务、长时间运行的任务、系统维护或监控等。它们在后台运行,不需要用户的直接参与或操作,而且可以持续运行,即使用户退出或注销系统。

启动线程

我们上面只是创建了线程,要想让线程真正的起作用,我们需要手动启动线程。线程对象.start()

class MyRunnable implements Runnable {    @Override    public void run() {        System.out.println("这是一个线程");    }}public class ThreadDemo2 {    public static void main(String[] args) {        Thread t = new Thread(new MyRunnable());        t.start();    }}

在这里插入图片描述

这里有人看到输出结果可能会问了,这跟我直接调用 run 方法好像没什么区别吧?我们这个代码肯定看不出来区别,所以我们稍稍修改一下代码。

class MyRunnable implements Runnable {    @Override    public void run() {        while(true) {            System.out.println("hello MyThread!");            try {                Thread.sleep(1000);            } catch (InterruptedException e) {                throw new RuntimeException(e);            }        }    }}public class ThreadDemo2 {    public static void main(String[] args) {        Thread t = new Thread(new MyRunnable());        t.start();        while(true) {            System.out.println("hello main!");            try {                Thread.sleep(1000);            } catch (InterruptedException e) {                throw new RuntimeException(e);            }        }    }}

这里 Thread.sleep() 的作用是使线程停止一会,防止进行的太快,我们不容易看到结果,并且这里的 Thread.sleep() 方法还需要我们抛出异常

在这里插入图片描述

我们可以看到这里的执行结果是 main 线程和 t 线程都执行了,而不是只执行其中的一个线程。不仅如此,这两个线程之间没有什么规定的顺序执行,而是随机的,这种叫做抢占式执行,每个线程都会争抢资源,所以会导致执行顺序的不确定,也正是因为多线程的抢占式执行,会导致后面的线程安全问题。

那么我们再来看看,如果直接调用 run 方法,而不是 start 方法会有什么结果。

在这里插入图片描述

当直接调用 run 方法的话,也就只会执行 t 对象的 run 方法,而没有执行 main 方法后面的代码,也就是说:当直接调用 run 方法的时候,线程并没有真正的启动,只有调用 start 方法,线程才会启动。

我们也可以通过 Java 自带的 jconsle 来查看当前有哪些Java进程。

我们需要找到 jconsole.exe 可执行程序。通常在这个目录下C:\Program Files\Java\jdk1.8.0_192\bin
在这里插入图片描述

我们也可以点进来看看。

在这里插入图片描述

在这里插入图片描述

终止线程

通常当主线程 main 执行完 mian 方法之后或者其他线程执行完 run 方法之后,线程就会终止,但是我们也可以在这之前手动终止线程。但是我们这里终止线程并不是立刻终止,也就相当于这里只是建议他这个线程停止,具体要不要停止得看线程的判断。

  • 自定义标志位来终止线程
  • 使用 Thread 自带的标志位来终止线程

1.自定义标志位终止线程

public class ThreadDemo4 {    private static boolean flg = false;  //定义一个标志位    public static void main(String[] args) {        Thread t = new Thread(() -> {            while(!flg) {                System.out.println("hello mythread!");                try {                    Thread.sleep(1000);                } catch (InterruptedException e) {                    throw new RuntimeException(e);                }            }        });        t.start();        System.out.println("线程开始");        try {            Thread.sleep(1000);        } catch (InterruptedException e) {            throw new RuntimeException(e);        }        flg = true;  /修改标志位使线程终止        System.out.println("线程结束");    }}

在这里插入图片描述

2.使用 Thread 自带的标志位终止线程

可以使用 线程对象.interrupt() 来申请终止线程。并且使用 Thread.currentThread,isInterrupted() 来判断是否终止线程。

  • Thread.currentThread() 获取到当前线程对象
public class ThreadDemo4 {    private static boolean flg = false;    public static void main(String[] args) {        Thread t = new Thread(() -> {            while(!Thread.currentThread().isInterrupted()) {                System.out.println("hello mythread!");                try {                    Thread.sleep(1000);                } catch (InterruptedException e) {                    e.printStackTrace();                }            }        });        t.start();        System.out.println("线程开始");        try {            Thread.sleep(1000);        } catch (InterruptedException e) {            throw new RuntimeException(e);        }        t.interrupt();        System.out.println("线程结束");    }}

在这里插入图片描述

发现了没,这里抛出了异常,但是线程并没有终止,为什么呢?问题出在哪里呢?

其实这里问题出在 Thread.sleep 上,如果线程在 sleep 中休眠,此时调用 interrupt() 会终止休眠,并且唤醒该线程,这里会触发 sleep 内部的异常,所以我们上面的运行结果就抛出了异常。那么为什么线程又被唤醒了呢?

interrupt 会做两件事:

  1. 把线程内部的标志位给设置成true,也就是 !Thread.current.isInterrupt() 的结果为true
  2. 如果线程在进行 sleep ,就会触发吟唱,把 sleep 唤醒

但是 sleep 在唤醒的时候,还会做一件事,把刚才设置的这个标志位,再设置回false(清空标志位),所以就导致了线程继续执行。那么如何解决呢?

很简单,因为 sleep 内部发生了异常,并且我们捕获到了异常,所以我们只需要在 catch 中添加 break 就行了。

try {     Thread.sleep(1000);    } catch (InterruptedException e) {        e.printStackTrace();        break;    }

在这里插入图片描述

这也就相当于,我 t 线程拒绝了你的终止请求。

线程等待

在多线程中,可以使用 线程对象.join() 来使一个线程等待另一个线程执行完或者等待多长时间后再开始自己的线程。

方法说明
public void join()等待线程结束
public void join(long millis)等待线程结束,最多等 millis 毫秒
public void join(long millis,int nanos)同理,但可以更高精度
public class ThreadDemo5 {    public static void main(String[] args) {        Thread t = new Thread(() -> {            for(int i = 0; i < 5; i++) {                System.out.println("hello mythread!");                try {                    Thread.sleep(1000);                } catch (InterruptedException e) {                    throw new RuntimeException(e);                }            }        });        t.start();        try {            t.join();        } catch (InterruptedException e) {            throw new RuntimeException(e);        }        for(int i = 0; i < 5; i++) {            System.out.println("hello main!");            try {                Thread.sleep(1000);            } catch (InterruptedException e) {                throw new RuntimeException(e);            }        }    }}

在这里插入图片描述

在那个线程中调用的 线程对象.join() 就是哪个线程等待,而哪个线程调用 join() 方法,那么这个线程就是被等待的。而这个等待的过程也被称为阻塞。如果在执行 join 的时候,调用 join 方法的线程如果已经结束了,那么就不会发生阻塞。

来源地址:https://blog.csdn.net/m0_73888323/article/details/132262494

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

什么是多线程?进程和线程的区别是什么?如何使用Java实现多线程?

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

什么是多线程?进程和线程的区别是什么?如何使用Java实现多线程?

文章目录 前言我们为什么要使用线程而不是进程来实现并发编程什么是线程进程和线程的区别如何使用Java实现多线程创建线程1.创建一个继承 Thread 类的线程类2.实现 Runnable 接口匿名内部类方式实现 Runnable
2023-08-19

Python中多线程、多进程、协程的区别是什么

今天就跟大家聊聊有关Python中多线程、多进程、协程的区别是什么,可能很多人都不太了解,为了让大家更加了解,小编给大家总结了以下内容,希望大家根据这篇文章可以有所收获。首先我们写一个简化的爬虫,对各个功能细分,有意识进行函数式编程。下面代
2023-06-16

Java使用Runnable和Callable实现多线程的区别是什么

这篇文章主要介绍“Java使用Runnable和Callable实现多线程的区别是什么”,在日常操作中,相信很多人在Java使用Runnable和Callable实现多线程的区别是什么问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作
2023-07-02

线程和进程的区别是什么

线程和进程的区别:1、线程是进程的一部分,用于实现并发和并行操作,而线程共享进程的资源,通信更方便快捷,切换开销较小;2、进程相对独立,需要通过显式机制进行通信,切换开销较大;而线程的管理更为灵活,进程的管理相对复杂。线程和进程是操作系统中
2023-08-10

Python中的多进程编程和多线程编程的区别是什么?

Python中的多进程编程和多线程编程的区别是什么?在Python中,多进程编程和多线程编程都是实现并行计算的方法。虽然它们都能同时运行多个任务,但其底层原理和使用方式却有所不同。多进程编程是利用操作系统的多进程机制来实现并行计算的。在Py
2023-10-22

Java之进程和线程的区别是什么

这篇文章主要介绍“Java之进程和线程的区别是什么”,在日常操作中,相信很多人在Java之进程和线程的区别是什么问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”Java之进程和线程的区别是什么”的疑惑有所帮助!
2023-07-05

java进程、线程、纤程的区别是什么

本篇内容主要讲解“java进程、线程、纤程的区别是什么”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“java进程、线程、纤程的区别是什么”吧!在Java 中,这些短小的代码段一般会被放入一个cl
2023-06-16

java中多线程和线程安全是什么

这篇文章给大家分享的是有关java中多线程和线程安全是什么的内容。小编觉得挺实用的,因此分享给大家做个参考,一起跟随小编过来看看吧。什么是进程?电脑中时会有很多单独运行的程序,每个程序有一个独立的进程,而进程之间是相互独立存在的。比如下图中
2023-06-25

java多线程守护线程的实现方法是什么

本篇内容介绍了“java多线程守护线程的实现方法是什么”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!lass StopThread impl
2023-06-04

python中多进程与多线程有什么区别

python中多进程与多线程有什么区别?相信很多没有经验的人对此束手无策,为此本文总结了问题出现的原因和解决方法,通过这篇文章希望你能解决这个问题。python的数据类型有哪些?python的数据类型:1. 数字类型,包括int(整型)、l
2023-06-14

java实现多线程的方式是什么

今天小编给大家分享一下java实现多线程的方式是什么的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考一下,希望大家阅读完这篇文章后有所收获,下面我们一起来了解一下吧。实现多线程的方式:1、继承
2023-07-04

Java中使用Thread类和Runnable接口实现多线程的区别是什么

这篇“Java中使用Thread类和Runnable接口实现多线程的区别是什么”文章的知识点大部分人都不太理解,所以小编给大家总结了以下内容,内容详细,步骤清晰,具有一定的借鉴价值,希望大家阅读完这篇文章能有所收获,下面我们一起来看看这篇“
2023-07-02

java多线程的实现方法是什么

在Java中实现多线程有两种方法:继承Thread类:定义一个类继承Thread类,并重写run()方法,在run()方法中编写线程的任务逻辑。然后创建该类的实例并调用start()方法启动线程。public class MyThread
java多线程的实现方法是什么
2024-03-04

C#中异步和多线程的区别是什么

本篇内容介绍了“C#中异步和多线程的区别是什么”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!一、区别和联系异步和多线程有什么区别?其实,异步
2023-06-30

java使用多线程的条件是什么

使用多线程的条件有以下几个:1. 程序需要并发执行多个任务或同时处理多个请求。2. 程序中存在可分解为独立子任务的任务集合。3. 程序中的某些任务需要等待很长时间的IO操作(如网络请求、文件读写),可以利用多线程来提高程序的响应性。4. 程
2023-09-26

python多线程多进程的优缺点是什么

Python多线程和多进程的优缺点如下:多线程的优点:1. 轻量级:线程的创建和上下文切换比进程要快得多,占用的资源也比较少。2. 共享内存:多个线程可以共享进程的内存空间,方便数据的传递和共享。3. 适合I/O密集型任务:多线程适合处理I
2023-05-30

Java多线程是什么意思

这篇文章主要讲解了“Java多线程是什么意思”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“Java多线程是什么意思”吧!多线程(multiple thread)是计算机实现多任务并行处理的一
2023-06-02

unity协程和线程的区别是什么

Unity协程和线程的区别主要体现在以下几个方面:1. 执行环境:- Unity协程运行于主线程中,与Unity的更新循环同步,可以访问Unity的API和资源。- 线程是在独立的线程中执行的,与主线程异步进行,无法直接访问Unity的AP
2023-09-29

编程热搜

  • Python 学习之路 - Python
    一、安装Python34Windows在Python官网(https://www.python.org/downloads/)下载安装包并安装。Python的默认安装路径是:C:\Python34配置环境变量:【右键计算机】--》【属性】-
    Python 学习之路 - Python
  • chatgpt的中文全称是什么
    chatgpt的中文全称是生成型预训练变换模型。ChatGPT是什么ChatGPT是美国人工智能研究实验室OpenAI开发的一种全新聊天机器人模型,它能够通过学习和理解人类的语言来进行对话,还能根据聊天的上下文进行互动,并协助人类完成一系列
    chatgpt的中文全称是什么
  • C/C++中extern函数使用详解
  • C/C++可变参数的使用
    可变参数的使用方法远远不止以下几种,不过在C,C++中使用可变参数时要小心,在使用printf()等函数时传入的参数个数一定不能比前面的格式化字符串中的’%’符号个数少,否则会产生访问越界,运气不好的话还会导致程序崩溃
    C/C++可变参数的使用
  • css样式文件该放在哪里
  • php中数组下标必须是连续的吗
  • Python 3 教程
    Python 3 教程 Python 的 3.0 版本,常被称为 Python 3000,或简称 Py3k。相对于 Python 的早期版本,这是一个较大的升级。为了不带入过多的累赘,Python 3.0 在设计的时候没有考虑向下兼容。 Python
    Python 3 教程
  • Python pip包管理
    一、前言    在Python中, 安装第三方模块是通过 setuptools 这个工具完成的。 Python有两个封装了 setuptools的包管理工具: easy_install  和  pip , 目前官方推荐使用 pip。    
    Python pip包管理
  • ubuntu如何重新编译内核
  • 改善Java代码之慎用java动态编译

目录