本文共 3023 字,大约阅读时间需要 10 分钟。
在操作系统中,进程和线程是资源分配和调度的两种核心概念。进程是操作系统分配资源(如CPU、内存)的基本单位,而线程则是处理CPU调度的最小单位。理解这两者的区别对于编写并发程序至关重要。
进程:相比线程,进程的执行资源需求更大,常被看作一个应用程序的运行环境。通过启动一个进程,可以运行一个独立的程序。例如,打开一个文件资源管理器或浏览器就是启动了一个新的进程。
线程:线程则更小,是CPU调度的基本单位。每个线程共享进程的资源,可以独立执行任务。一个进程中可以运行多个线程,线程之间通过共享堆内存进行通信和同步。线程的数量直接关系到程序的并行执行能力。
现代计算机的多核处理器支持多线程并行执行,但线程与CPU核心的关系并非线性直接关系。在单线程程序中,每个核心都可以执行一个线程。但现代处理器支持超线程技术,使得一个核心内核可以同时处理多个逻辑核心线程。
需要注意的是,与内核数量关系不大,线程的调度主要依赖操作系统的轮转机制(RR调度)。
操作系统通过CPU时间片轮转机制进行进程调度,将任务分配给各个核心。每个线程获得一定的CPU时间片后执行,只有在超时后才被替换,释放所占用的CPU。
根据测量结果,1.6GHz的CPU执行一个操作instructions(约 resurgence time)大约需要0.6纳秒。该时间间隔对于线程间切换来说是关键参数。上下文切换需要耗费大量时间,尤其是内核态操作,每次切换需要数以万计的CPU周期。
计算机科学中的并行和并发概念常被互换,但二者有根本差异:
并行:涉及能够同时运行多个任务。例如,多核处理器可以同时处理多个线程,这些线程互不干扰地执行不同的任务。
并发:指单时间内可以处理的任务数量。并发与并行的区别在于时序,任务是交替执行的,而非本部件层面同时运行。
理解这一点至关重要。并行更多是指任务本身的并行执行,而并发更多关注时间维度上的处理数量。
高并发编程能显著提升系统性能,解决性能瓶颈,并简化代码结构。常见优势:
资源充分利用:通过并行处理,充分利用CPU、内存等资源,提升性能表现。
提升用户体验:快速响应用户请求,减少等待时间。
代码简化:通过任务异步化和模块化设计,便于系统扩展和维护。
然而,高并发编程也伴随着潜在问题:
线程安全:线程共享资源,可能导致竞态条件和死锁。需通过锁机制和其他同步方式加以防范。
性能开销:频繁的线程调度和上下文切换会增加系统负担,影响性能。
资源限制:每个进程(如Linux)最多支持1000个线程,需谨慎管理资源分配。
推荐使用线程池进行高并发处理,以控制并发深度并优化资源利用率。
在Java中,可以通过Thread
类或Runnable
接口来创建线程:
Thread
类Thread
类提供了线程创建和管理的底层功能。创建方式:
new Thread(task);
适用于需要控制线程生命周期、带有异常处理能力的场景。
Runnable
接口Runnable
更注重任务本身的逻辑实现,适合异步执行:
new Thread(task).start();
叶目标是让任务在独立线程中运行。
两者的区别在于,Thread
类提供了解决并发问题的完整控制,而Runnable
更注重对业务逻辑的抽象。
线程中断是协作式多线程编程中的重要机制,为线程提供协调退出方式。
stop()
方法的弊端stop()
方法要求线程主动释放资源,但其调用的线程可能无法释放资源而导致内存泄漏。
使用integrated();
方法对线程发出中断信号。
由于InterruptedException
可能不会导致线程终止,需结合interrupted()
方法判断线程的中断状态。
非 daemon 线程(后台线程)在中断后仍需主动终止。需在异常处理中添加interrupt()
方法,确保线程终止。
class MyThread extends Thread { @Override public void run() { try { while (!isInterrupted()) { sleep(1000); // sleep 不抛出 InterruptedException } } catch (InterruptedException e) { e.printStackTrace(); interrupted(); // 重置中断标志位 start(); // 供其他线程中断信息处理时调用 } }}
线程从启动到终止经历了以下阶段:
线程管理不当可能导致资源泄漏和性能问题。谨慎使用join()和其他阻塞方法。
线程安全是并发编程的重要关注点。Synchronized(内置锁)主要用于保护共享资源的可见性和排他性,防止多线程竞骤访问和修改。
它可以修饰方法或代码块。通过匿名锁机制,可以实现更细粒度的锁控制。
Volatile目标变量具有可见性和一致性,但不保证原子性。在多线程环境下,一写多读时需要保护读写锁,可以通过Volatile变量配合其他机制(如atomic references)实现可靠的写入和读取。
ThreadLocal允许每个线程获得自己的属性副本,使其线程独立,避免共享资源冲突。常见用途包括抢抓资源和事务处理。正确管理ThreadLocal变量至关重要,以防止内存泄漏和资源竞争。建议及时释放变量,避免存储大量未使用的keyup。
线程间需通过协作完成任务,常用的机制包括等待通知组以及信号代理。生产者-消费者模型是线程协作的典型案例。
等待方:在不满意条件时调用对象的wait()方法。
通知方:修改条件后调用notify()或notifyAll()。
调用这些方法需联任有对象锁,否则可能导致并发问题。
Join()方法可以让线程等待另一个线程运行完毕。这对于线程的正序执行至关重要。
本文系统介绍了线程基础、并发编程、高级线程管理和安全性机制等核心内容,提供了实践的代码示例和理论分析。理解线程运作机制与底层原理,是掌握并发编程的基石。
转载地址:http://xqbez.baihongyu.com/