线程与进程、并发与并行——概念篇

本文章主要讲述线程、进程、并发和并行的基本概念以及常用例子,接着会阐述它们之间的关系以及应用场景。主要有以下内容:进程(概念、组成、特征、状态)、线程(概念、特征、创建)、进程与线程的关系、并发(概念、特征、实现方式)、并行(概念、特性、实现方式)、并发与并行的关系。

一、进程

(1)概念

简单来说,进程是一个程序在某个数据集上的一次动态执行活动,是一个独立的运行实体。操作系统会通过进程来管理和调度程序的运行。
比如:当你打开一个浏览器时,浏览器程序会启动一个或多个进程。每个进程都在执行浏览器的相关功能,比如显示网页、播放视频等。

(2)组成

程序代码:进程执行时的基础;
数据:输入、中间和输出;
上下文信息:进程运行时的状态信息;

(3)特征

并发性多个进程可以同时运行。(例如:在Web服务器上,多个用户同时访问网站时,服务器会为每个用户的请求创建一个进程(或线程),这些进程同时运行,处理不同的用户请求,从而实现高并发的服务。)

动态性进程从创建到运行再到结束,是一个动态的过程,是有生命周期的。一个程序只有在运行时才会成为一个进程。(例如:在Windows系统的任务管理器中,你可以看到当前运行的所有进程。这些进程的数量和状态会随着程序的启动和关闭而不断变化,这就是进程动态性的直观体现。)

独立性每个进程有独属于自己的地址空间和资源,一个进程的运行不会影响到其它进程的运行。(例如:在电脑上同时运行Photoshop和Word时,Photoshop的进程和Word的进程是独立的。它们各自有自己的内存空间、文件资源和运行状态。)

异步性进程的运行速度和完成时间是不可预知的,它们按照各自独立的、不可预知的速度向前推进。(例如:假设你同时启动了两个进程:一个用于下载文件,另一个用于压缩文件。这两个进程的完成时间是不可预知的。下载文件的速度取决于网络状况,而压缩文件的速度取决于文件大小和计算机性能。你无法准确预测哪个进程会先完成,这就是进程的异步性。)

(4)状态

运行态(Running):进程正在处理器上运行。
就绪态(Ready):进程已经准备好运行,但由于没有获得处理器资源而暂时不能运行。一旦获得处理器资源,进程就可以进入运行态。
阻塞态(Blocked):进程因为等待某种事件(如输入/输出操作完成、等待信号量等)而不能运行。只有等待的事件发生后,进程才能进入就绪态。

二、线程

(1)概念

线程是程序执行流的最小单元,是操作系统能够进行调度的最小单位。它比进程更轻量级,用于实现程序中的并发操作。线程是运行在进程内部的,一个进程可以包含多个线程。
举例: 在一个浏览器进程中,可以有多个线程,一个线程用于渲染网页,一个线程用于处理用户输入,另一个线程用于下载网络资源。这些线程共享浏览器进程的资源,但可以并发运行。

(2)特性

轻量级: 资源占用少。(举例: 在多线程的Web服务器中,每个用户请求可以分配一个线程来处理。由于线程的轻量级特性,服务器可以同时处理大量用户请求,而不会因为线程切换而产生过多的性能开销。)

并发性:线程是并发执行的,多个线程可以同时运行。
(举例: 在一个视频播放软件中,一个线程用于解码视频,另一个线程用于渲染视频画面,还有一个线程用于处理用户交互(如暂停、快进)。)

共享性:同一进程内的线程共享进程的资源,包括内存、文件句柄、全局变量等
(举例:在一个多线程的下载工具中,多个线程可以同时下载文件的不同部分,这些线程共享同一个文件缓冲区,将下载的内容写入同一个文件。)

独立性:线程是独立调度的单位,每个线程有自己的运行状态(如运行态、就绪态、阻塞态)。一个线程的阻塞不会影响其他线程的运行。
(举例:在一个聊天软件中,一个线程用于接收消息,另一个线程用于发送消息。如果接收消息的线程因为网络延迟而阻塞,发送消息的线程仍然可以正常运行。)

(3)线程的创建

CHOOSE1——通过实现Runnable接口创建线程:Runnable接口是Java中用于定义线程任务的标准接口。通过实现Runnable接口,可以将线程任务与线程的执行逻辑分离,这种方式更灵活,也更符合面向对象的设计原则。

public class RunnableExample {
    public static void main(String[] args) {
        // 创建一个Runnable任务
        Runnable myTask = new MyRunnable();

        // 创建线程并传入任务
        Thread thread1 = new Thread(myTask);
        Thread thread2 = new Thread(myTask);

        // 启动线程
        thread1.start();
        thread2.start();
    }
}

// 实现Runnable接口
class MyRunnable implements Runnable {
    @Override
    public void run() {
        // 定义线程的执行逻辑
        for (int i = 0; i < 5; i++) {
            System.out.println("Thread " + Thread.currentThread().getName() + " is running: " + i);
        }
    }
}

说明:
1.MyRunnable类实现了Runnable接口,并重写了run()方法,定义了线程的执行逻辑。
2.创建Thread对象时,将Runnable任务作为参数传入。
3.调用start()方法启动线程,线程会自动调用Runnable的run()方法。

CHOOSE2——通过继承Thread类创建线程:Java的Thread类本身也提供了一个run()方法,因此可以通过继承Thread类并重写run()方法来创建线程。这种方式更简单,但灵活性稍差,因为Java不支持多重继承,一个类只能继承一个父类。

public class ThreadExample {
    public static void main(String[] args) {
        // 创建线程对象
        MyThread thread1 = new MyThread("Thread-1");
        MyThread thread2 = new MyThread("Thread-2");

        // 启动线程
        thread1.start();
        thread2.start();
    }
}

// 继承Thread类
class MyThread extends Thread {
    public MyThread(String name) {
        // 设置线程名称
        super(name);
    }

    @Override
    public void run() {
        // 定义线程的执行逻辑
        for (int i = 0; i < 5; i++) {
            System.out.println(getName() + " is running: " + i);
        }
    }
}

说明:
1.MyThread类继承了Thread类,并重写了run()方法。
2.在run()方法中定义了线程的执行逻辑。
3.调用start()方法启动线程,线程会自动执行run()方法。

CHOOSE3——使用Lambda表达式创建线程(Java 8及以上):从Java 8开始,可以使用Lambda表达式简化线程的创建,尤其是当线程任务较简单时。

public class LambdaThreadExample {
    public static void main(String[] args) {
        // 使用Lambda表达式创建线程
        Thread thread1 = new Thread(() -> {
            for (int i = 0; i < 5; i++) {
                System.out.println("Thread-1 is running: " + i);
            }
        });

        Thread thread2 = new Thread(() -> {
            for (int i = 0; i < 5; i++) {
                System.out.println("Thread-2 is running: " + i);
            }
        });

        // 启动线程
        thread1.start();
        thread2.start();
    }
}

说明:
1.使用Lambda表达式直接定义线程的执行逻辑。
2.这种方式简单且代码更简洁,适合线程任务较简单的情况。

三、进程和线程的关系

(1)联系

线程是进程的子实体:一个进程可以包含多个线程,线程运行在进程的上下文中。

(2)区别

资源分配:进程是资源分配的单位,线程是CPU调度的单位。进程有自己的独立地址空间,而线程共享进程的地址空间。
切换代价:线程切换的代价比进程切换小,因为线程切换不需要切换地址空间。
独立性:进程是独立的运行实体,一个进程的崩溃不会影响其他进程;线程是依赖于进程的,一个线程的崩溃可能会导致整个进程崩溃。
举例:
在一个多线程的数据库管理系统中,多个线程可以同时处理不同的用户请求。这些线程共享数据库进程的资源(如内存、数据库连接池等),但每个线程独立处理用户请求。

四、并发

(1)概念

并发是指多个任务或活动在同一时间段内同时进行,但这些任务可能并不是同时运行的。
换句话说,多个任务在宏观上看起来是同时发生的,但实际上它们可能是交替执行的。并发的核心在于任务的并行性和资源的共享性。
例如:GUI程序通常有一个主线程用于处理用户界面事件(如按钮点击、窗口绘制)。同时,程序可能会创建多个后台线程用于执行耗时任务(如文件加载、数据计算)。主线程和后台线程通过时间片轮转的方式交替运行。
并发体现:用户界面操作和后台任务在宏观上看起来是同时进行的,但实际上它们是交替执行的。

(2)实现方式

多进程并发:操作系统创建多个进程,每个进程执行不同的任务。虽然在单核处理器上,多个进程是通过时间片轮转的方式交替运行的,但从宏观上看,它们是并发的。在多核处理器上,多个进程可以真正并行运行。

多线程并发:一个进程内部创建多个线程,线程共享进程的资源,但可以并发执行。线程的切换开销比进程小,因此更适合高并发场景。

(3)特征

1.并行性(Parallelism)
并行性是并发的一个特例,指的是多个任务同时运行。在多核处理器上,多个线程或进程可以真正并行运行,每个核心执行一个任务。例如,一个四核处理器可以同时运行四个线程。
2. 交替执行(Interleaving)
在单核处理器上,多个任务通过时间片轮转的方式交替执行。
3. 资源共享(Resource Sharing)
并发任务通常共享某些资源,如内存、文件、CPU时间等。资源共享可以提高效率,但也会带来数据竞争和同步问题。例如,多个线程可能需要访问同一个文件或内存区域,这就需要同步机制来避免冲突。
4. 异步性(Asynchrony)
并发任务的执行顺序和完成时间是不可预知的。任务的执行速度可能因资源可用性、输入/输出操作等因素而不同。例如,一个线程可能因为等待磁盘I/O操作而阻塞,而其他线程继续运行。

五、并行

(1)概念

并行(Parallelism)是指多个任务或活动在同一时刻真正同时运行。

(2)实现方式

并行的实现依赖于多核处理器或多台计算机的硬件支持,使得多个任务可以同时进行,从而提高系统的整体性能。

(3)特征

同时运行:并行任务在物理上同时执行,而不是交替执行。
硬件支持:并行通常依赖于多核处理器或多台计算机的集群。
资源独立性:每个任务通常分配独立的资源(如CPU核心、内存等)。
高效性:并行可以显著提高计算密集型任务的效率,因为多个任务可以同时处理。

六、并发与并行的关系

1.并发是更广泛的概念,指的是多个任务在宏观上看起来是同时进行的,但任务实际上是交替执行的。并发的目标是提高系统的响应性和资源利用率。
2.并行是并发的一个特例,指的是多个任务在物理上同时运行。并行的目标是提高系统的计算能力。
3.并发和并行可以同时存在,例如在多核处理器的系统中,多个任务可以同时运行(并行),而每个任务内部又可以有多个子任务交替执行(并发)。
4.并发和并行的实现方式不同,并发依赖于操作系统的时间片轮转,而并行依赖于硬件支持。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值