Java中进程与线程的区别

Java中进程与线程的区别

在Java中,进程(Process)和线程(Thread)都是常见的概念。虽然它们的功能类似,但它们之间存在明显的不同。了解它们的区别对我们正确地设计和编写多线程程序非常重要。

进程和线程的定义

进程是操作系统操作的基本单位,它是程序执行时的一个实例。它拥有自己的内存空间、系统资源和进程上下文等。每个进程都有一个或多个线程,线程是运行在进程内部的一个独立顺序的控制单元,也是CPU调度的最小单位。

进程和线程的区别

下面是进程和线程的几点区别:

资源分配

进程是操作系统中的一个独立单位,它拥有独立的内存空间和其他系统资源,进程之间相互独立。而线程则是进程中的一个执行流,在同一个进程中的线程共享进程的内存空间和其他系统资源。

调度和切换

进程之间的切换需要进行上下文切换,这是由操作系统的调度器实现的,上下文切换时需要保存和恢复进程的上下文信息,效率较低。而线程之间的切换时不需要进行上下文切换,因为它们共享进程的上下文信息。

独立性

进程之间是独立的,一个进程的崩溃不会影响其他进程。而线程之间是相互依赖的,一个线程的崩溃可能导致整个进程的崩溃或不稳定。

开销

由于线程共享了相同的内存和系统资源,所以创建和销毁线程的开销比进程小。

示例

示例一:多线程下载文件

一个典型的多线程程序就是文件下载器。下载器首先建立一个单独的线程,来管理整个下载过程,然后将文件下载分成多个部分,每个部分交给一个线程下载,最后将所有下载结果合并。

public class Downloader {
    private String url;
    private int threadCount;

    public Downloader(String url, int threadCount) {
        this.url = url;
        this.threadCount = threadCount;
    }

    public void download() throws Exception {
        URL u = new URL(url);
        URLConnection conn = u.openConnection();
        int totalSize = conn.getContentLength();
        int blockSize = totalSize / threadCount;
        List<DownloadThread> threads = new ArrayList<>();
        for (int i = 0; i < threadCount; i++) {
            int startPos = i * blockSize;
            int endPos = (i == threadCount - 1) ? totalSize - 1 : (i + 1) * blockSize - 1;
            DownloadThread thread = new DownloadThread(url, startPos, endPos);
            threads.add(thread);
            thread.start();
        }
        for (DownloadThread thread : threads) {
            thread.join();
        }
        merge(totalSize);
    }

    private void merge(int totalSize) throws Exception {
        File file = new File("download.txt");
        FileOutputStream fos = new FileOutputStream(file);
        for (int i = 0; i < threadCount; i++) {
            String fileName = "part" + i;
            FileInputStream fis = new FileInputStream(fileName);
            byte[] buffer = new byte[BUFFER_SIZE];
            int len;
            while ((len = fis.read(buffer)) != -1) {
                fos.write(buffer, 0, len);
            }
            fis.close();
            new File(fileName).delete();
        }
        fos.close();
    }

    private class DownloadThread extends Thread {
        private String url;
        private int startPos;
        private int endPos;

        public DownloadThread(String url, int startPos, int endPos) {
            this.url = url;
            this.startPos = startPos;
            this.endPos = endPos;
        }

        public void run() {
            try {
                HttpURLConnection conn = (HttpURLConnection) new URL(url).openConnection();
                conn.setRequestProperty("Range", "bytes=" + startPos + "-" + endPos);
                InputStream is = conn.getInputStream();
                FileOutputStream fos = new FileOutputStream("part" + getId());
                int len;
                byte[] buffer = new byte[BUFFER_SIZE];
                while ((len = is.read(buffer)) != -1) {
                    fos.write(buffer, 0, len);
                }
                fos.close();
                is.close();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    private static final int BUFFER_SIZE = 4096;

    public static void main(String[] args) throws Exception {
        Downloader downloader = new Downloader("http://localhost/test.txt", 4);
        downloader.download();
    }
}

示例二:多进程计算

假设我们需要计算 $1+2+\cdots+10^9$ 的和,这个计算任务本身就非常耗时,如果用单进程单线程来计算需要很长时间。我们可以将这个任务分为10个小任务,交给10个不同的进程来计算,最后将结果求和。

public class Sum {
    public static void main(String[] args) {
        final int N = 1000000000;
        final int PROCESS_COUNT = 10;
        final int BLOCK_SIZE = N / PROCESS_COUNT;

        long start = System.currentTimeMillis();

        SumTask[] tasks = new SumTask[PROCESS_COUNT];
        for (int i = 0; i < PROCESS_COUNT; i++) {
            int startNum = i * BLOCK_SIZE + 1;
            int endNum = (i == PROCESS_COUNT - 1) ? N : (i + 1) * BLOCK_SIZE;
            tasks[i] = new SumTask(startNum, endNum);
            tasks[i].start();
        }

        long sum = 0;
        try {
            for (SumTask task : tasks) {
                task.join();
                sum += task.getResult();
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        long end = System.currentTimeMillis();

        System.out.println("sum=" + sum);
        System.out.println("time=" + (end - start));
    }

    private static class SumTask extends Thread {
        private int startNum;
        private int endNum;
        private long result;

        public SumTask(int startNum, int endNum) {
            this.startNum = startNum;
            this.endNum = endNum;
        }

        public void run() {
            for (int i = startNum; i <= endNum; i++) {
                result += i;
            }
        }

        public long getResult() {
            return result;
        }
    }
}

总结

进程和线程都是并发编程中常见的概念,它们分别代表了操作系统中的最小运行单元和CPU中的最小调度单元。了解它们的区别对我们正确地设计和编写多线程程序非常重要。在实际的应用中,我们往往需要根据实际情况选择进程或线程,来达到最优的编程目标。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:Java中进程与线程的区别 - Python技术站

(0)
上一篇 2023年5月19日
下一篇 2023年5月19日

相关文章

  • SpringBoot使用JdbcTemplate操作数据库

    SpringBoot使用JdbcTemplate操作数据库攻略 什么是JdbcTemplate JdbcTemplate是Spring Framework中的一个类,它封装了对JDBC的使用,提供了使用非常规范、灵活简单的方式来操作数据库。 使用JdbcTemplate可以避免我们手动编写JDBC代码,使得我们能够更专注于业务逻辑,从而提高效率。 JdbcT…

    Java 2023年5月20日
    00
  • 在SpringBoot项目中利用maven的generate插件

    以下是利用maven的generate插件在SpringBoot项目中的完整攻略,包含两个示例。 什么是maven的generate插件 maven的generate插件是一个代码生成插件,可以根据指定的模板文件和数据生成指定的代码文件。在SpringBoot项目中,我们可以利用generate插件来生成一些常用的代码,例如controller、servic…

    Java 2023年5月19日
    00
  • JSP实现用户登录、注册和退出功能

    下面是详细讲解“JSP实现用户登录、注册和退出功能”的完整攻略。 1. 前置条件 了解Java web开发相关知识 掌握Tomcat服务器的使用方式 下载并安装MySQL数据库 掌握JSP基本语法 2. 构建JSP Web项目 使用Eclipse等常用IDE创建一个JSP Web项目,配置好Tomcat服务器及数据库连接。 3. 数据库设计 用户信息在本案例…

    Java 2023年6月15日
    00
  • Win7系统脚步设置出现问题导致网页内容无法复制的解决方法

    当Win7系统脚步设置出现问题时,会导致网页内容无法复制或复制后格式混乱的情况。以下是解决此问题的步骤: 步骤一:检查剪贴板服务是否开启 按下’Win+R’键,输入’services.msc’,回车进入服务管理器界面。 在该界面中找到“剪贴板服务”并右键点击。选择属性,检查该服务是否已开启。 若该服务未开启,点击’启动’即可。 步骤二:清空剪贴板缓存 按下’…

    Java 2023年5月30日
    00
  • hadoop 全面解读自定义分区

    Hadoop 全面解读自定义分区 什么是分区 在 Hadoop 中,分区是指在将数据写入到 HDFS 中时,对数据进行分类以便于管理。在每个分区中,都包含了一部分数据,每个分区都有一个固定的编号。 默认分区 当我们使用 Hadoop 内置的 MR 程序时,所有的数据都将会按照默认的哈希分区规则进行分区。一般情况下,分区的数量是由系统自动计算的。 自定义分区 …

    Java 2023年5月20日
    00
  • 完整的医院就诊挂号系统基于Spring MVC + Spring + MyBatis实现

    完整的医院就诊挂号系统基于Spring MVC + Spring + MyBatis实现 医院就诊挂号系统是一个常见的医疗信息化应用,它可以帮助患者方便地预约挂号、查询医生信息、查看就诊记录等。本文将详细讲解如何使用 Spring MVC + Spring + MyBatis 框架实现一个完整的医院就诊挂号系统,包括如何设计数据库、如何实现业务逻辑、如何实现…

    Java 2023年5月18日
    00
  • 使用idea和gradle编译spring5源码的方法步骤

    下面就是详细的“使用idea和gradle编译spring5源码的方法步骤”的攻略: 1.准备工具 首先,我们需要准备好以下工具: JDK IDEA Gradle Spring5源码 具体版本根据自己的需求来选择,这里不再赘述。 2.导入Spring5源码 将Spring5源码下载下来,并用IDEA导入项目。如果是第一次使用Gradle编译该项目,需要等待I…

    Java 2023年5月26日
    00
  • Java 如何优雅的拷贝对象属性

    当我们需要在 Java 中将一个类的属性值赋值给另一个类时,常见的做法是手动逐个拷贝属性值。但是,这种方法在有大量属性需要拷贝时非常繁琐且容易出错。因此,我们需要一种更优雅的方式来完成对象属性的拷贝,下面是一种实现方式和示例说明。 使用 BeanUtils BeanUtils 是一款常用的 Java 工具包,其中提供了许多方便的工具方法,包括对象属性的复制和…

    Java 2023年5月26日
    00
合作推广
合作推广
分享本页
返回顶部