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

这篇具有很好参考价值的文章主要介绍了什么是多线程?进程和线程的区别是什么?如何使用Java实现多线程?。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

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

前言

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

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

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

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

开销不仅体现在时间上,还体现在内存和 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 接口。
什么是多线程?进程和线程的区别是什么?如何使用Java实现多线程?,JavaEE,java,JavaEE,多线程,进程
什么是多线程?进程和线程的区别是什么?如何使用Java实现多线程?,JavaEE,java,JavaEE,多线程,进程
什么是多线程?进程和线程的区别是什么?如何使用Java实现多线程?,JavaEE,java,JavaEE,多线程,进程

我们可以将 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 线程可以被用来分组管理,分好的组即为线程组,这个我们目前了解即可

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

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

Thread 的几个常见属性

属性 获取方法
ID getId()
名称 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();
    }
}

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

这里有人看到输出结果可能会问了,这跟我直接调用 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() 方法还需要我们抛出异常

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

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

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

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

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

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

我们需要找到 jconsole.exe 可执行程序。通常在这个目录下C:\Program Files\Java\jdk1.8.0_192\bin
什么是多线程?进程和线程的区别是什么?如何使用Java实现多线程?,JavaEE,java,JavaEE,多线程,进程

我们也可以点进来看看。

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

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

终止线程

通常当主线程 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("线程结束");
    }
}

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

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("线程结束");
    }
}

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

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

其实这里问题出在 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;
    }

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

这也就相当于,我 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);
            }
        }
    }
}

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

在那个线程中调用的 线程对象.join() 就是哪个线程等待,而哪个线程调用 join() 方法,那么这个线程就是被等待的。而这个等待的过程也被称为阻塞。如果在执行 join 的时候,调用 join 方法的线程如果已经结束了,那么就不会发生阻塞。文章来源地址https://www.toymoban.com/news/detail-646557.html

到了这里,关于什么是多线程?进程和线程的区别是什么?如何使用Java实现多线程?的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Python中进程和线程到底有什么区别?

    python 安装包+资料:点击此处跳转文末名片获取 一、进程和线程的关系 线程与进程的区别可以归纳为以下4点: 地址空间和其它资源(如打开文件) :进程间相互独立,同一进程的各线程间共享。某进程内的线程在其它进程不可见。 通信 :进程间通信IPC,线程间可以直接读写

    2023年04月08日
    浏览(42)
  • ⛳ 多线程面试-什么是多线程上下文切换?

    多线程会共同使用一组计算机上的CPU ,而线程数大于给线程分配的 CPU 数量时,为了让各个线程都有执行的机会,就需要轮转使用CPU。 不同的线程切换使用 CPU, 发生的切换数据等, 就是上下文切换 在上下文切换过程中, CPU 会停止处理当前运行的程序, 并保存当前程序运行的具

    2024年02月14日
    浏览(42)
  • 06 为什么需要多线程;多线程的优缺点;程序 进程 线程之间的关系;进程和线程之间的区别

    CPU、内存、IO之间的性能差异巨大 多核心CPU的发展 线程的本质是增加一个可以执行代码工人 多线程的优点 多个执行流,并行执行。(多个工人,干不一样的活) 多线程的缺点 上下文切换慢,切换上下文典型值1us vs 0.3ns/cycle CPU在执行A任务(A没有执行完)时,切换到任务B,需

    2024年02月14日
    浏览(50)
  • 什么是多线程环境下的伪共享(false sharing)?

    在Java中,伪共享(false sharing)是指多线程环境下,由于缓存一致性协议的影响,不同线程访问同一缓存行中的不同数据造成的性能下降现象。当多个线程同时访问不同变量,但这些变量存储在同一缓存行中时,每个线程只修改自己的变量,但由于缓存一致性协议的要求,需要将

    2024年02月10日
    浏览(38)
  • 什么是线程?线程和进程的关系?如何创建/查看线程?

    1.1.1 什么是线程 进程进一步细化为线程, 是程序内部的一条执行路径. 一个进程中至少有一个线程. 每个线程之间都可以按照顺讯执行自己的代码. 多个线程之间\\\"同时\\\"执行多份代码. 1.1.2 线程存在的意义 ① “并发编程\\\"成为\\\"刚需” 单核 CPU 的发展遇到了瓶颈. 要想提高算力, 就

    2024年02月15日
    浏览(42)
  • 详细说明一下Java中进程和线程的区别和联系

    Java中的进程和线程都是操作系统中执行代码的概念,但它们有以下区别: 进程是资源分配的最小单位,而线程是程序执行的最小单位。每个进程都有自己独立的内存空间、文件句柄等资源,而线程共享进程的资源。 进程之间相互独立,一个进程崩溃不会影响其他进程,而线

    2024年02月09日
    浏览(56)
  • 【Linux操作系统】Linux中的进程和线程的概念、区别和实现的细节

    在Linux中,进程和线程是操作系统中两个重要的执行单位。它们是对程序运行时的抽象概念,用于管理和调度计算机资源。 进程是正在运行的程序的实例。它是操作系统分配资源和调度执行的基本单位。每个进程都有自己独立的内存空间,包含代码、数据、堆栈等。进程还包

    2024年02月14日
    浏览(53)
  • 【面试精讲】Java动态代理是如何实现的?JDK Proxy 和 CGLib 有什么区别?

    Java动态代理是如何实现的?JDK Proxy 和 CGLib 有什么区别? 一、Java动态代理的实现 1、使用JDK Proxy实现动态代理 2、使用CGLib实现动态代理 二、JDK Proxy 与 CGLib 的区别 三、Spring中的动态代理 四、 Lombok代理原理 总结 本文深入探讨了Java动态代理的实现机制,分别介绍了使用JDK

    2024年03月14日
    浏览(46)
  • 操作系统进程线程(一)—进程线程协程区别、多进程多线程、进程调度算法、进程线程通信

    定义上 进程: 资源分配和拥有 的基本单位,是调度的基本单位。 运行一个可执行程序会创建一个或者多个进程;进程就是运行起来的程序 线程:程序 执行 基本单位,轻量级进程。 每个进程中都有唯一的主线程 ,主线程和进程是相互依赖的关系。 协程: 用户态 的轻量级

    2024年02月01日
    浏览(57)
  • 进程和线程的区别 && 线程之间共享的资源

    线程和进程都是操作系统中的执行单位,但它们在以下几个方面存在区别: 1.执行环境:线程和进程都有自己的执行上下文,包括程序计数器、寄存器和栈,可以独立执行指令。 2.并发性:线程和进程都可以实现并发执行,提高系统资源利用率。 3.调度:线程和进程都需要操

    2024年02月07日
    浏览(43)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包