
一、进程与线程基础1. 进程的概念进程是操作系统层面正在运行的应用程序也是系统分配硬件资源的最小单位。如今主流操作系统都支持多进程并发运行从微观视角来看各个进程会轮流抢占CPU时间片执行任务在宏观上就呈现出同时运行的效果这便是并发运行的特点。2. 线程的核心概念线程隶属于进程是进程内部独立运行的执行单元也常被称作轻量级进程它是操作系统进行CPU调度的基本单位。一个进程能够容纳多个线程同进程下的所有线程会共享进程持有的内存资源同时每个线程又会单独配备程序计数器、栈空间以及局部变量。正因如此线程之间传递数据、相互协作的成本要远远低于不同进程之间的交互。3. 主线程与线程组成程序启动之后系统会自动创建出主线程该线程以main方法作为执行入口掌控着整个程序的启动与终止流程。一个完整的线程包含几大核心组成部分。首先是CPU执行权只有成功获取CPU时间片的线程才有机会执行自身的业务代码。其次是数据存储结构线程之间共享堆内存里的对象数据而每个线程独有的栈空间则用来存放各自的局部变量保证数据相互隔离。最后是执行代码线程需要完成的业务逻辑都会编写在run方法或者call方法当中。二、多线程的实现方式1. 继承Thread类自定义实体类直接继承java.lang.Thread父类并重写类中的run方法在方法内部编写线程需要执行的任务逻辑。完成类的定义后手动创建线程实例对象想要启动线程必须调用start方法由Java虚拟机统一调度执行任务切忌直接调用run方法。这种实现方式存在明显局限由于Java语言不支持类的多继承如果当前类已经继承了其他父类就无法再使用该方式创建线程。2. 实现Runnable接口让自定义类实现java.lang.Runnable接口重写接口里的run方法来定义线程任务。创建对象时先实例化任务类对象再将该对象作为参数传入Thread的构造方法最终调用start方法启动线程。这种方式摆脱了单继承的限制将任务逻辑和线程本身分离开来结构更加灵活也可以让多个线程共用同一个任务对象实现资源共享。3. 实现Callable接口java.util.concurrent.Callable接口同样可以用来创建线程和Runnable不同的是它支持泛型返回值接口中的call方法不仅能定义任务还可以向外抛出异常。使用该接口时需要配合FutureTask类先把Callable实例封装为可被线程识别的任务对象再交给Thread启动。任务执行完毕后借助FutureTask的方法就能拿到最终的运行结果非常适合需要异步计算并获取返回数据的场景。三、线程的生命周期与常用方法1. 线程的六种状态线程从创建到消亡会经历完整的生命周期一共划分出六种状态。新建状态是指已经创建好线程对象但还没有调用启动方法此时线程并未开始工作。调用start方法后线程进入可运行状态该状态整合了就绪与运行两种情况线程等待CPU调度时为就绪状态抢到时间片后就转为运行状态。当线程尝试获取同步锁却暂时无法成功就会进入阻塞状态主动让出CPU资源直到成功拿到锁才会恢复运行。调用wait方法会让线程进入无限等待状态会一直停滞直到被其他线程唤醒。调用sleep、join这类方法则会让线程进入限时等待状态等待指定时长后便会自动恢复。当线程把所有代码执行完毕或是运行过程中出现异常终止就会进入终止状态代表线程生命周期结束。2. 常用线程方法解析调用sleep方法可以让当前线程暂停运行指定时长线程会进入限时等待状态期间释放CPU资源但不会释放自身持有的同步锁。join方法可以设定线程执行优先级当一个线程调用该方法后当前运行的线程会暂停等待目标线程全部执行完成后再继续往后运行。wait、notify以及notifyAll方法都定义在Object类中主要用于实现多个线程之间的通信协作。执行wait方法的线程会主动释放锁并进入等待状态。notify方法会随机唤醒一个处于等待状态的线程notifyAll则会唤醒所有等待中的线程这两个唤醒方法执行后当前线程依旧持有锁且三类方法都必须在同步环境中使用。四、线程同步与安全1. 线程安全问题的根源当多个线程同时访问同一份共享资源时线程的执行顺序无法人为控制。原本不可拆分的连续操作有可能被多个线程穿插执行破坏了操作的原子性最终造成数据内容错乱、结果不符合预期这就是典型的线程安全问题。2. 线程同步的实现方式同步代码块是解决线程安全的基础方式使用synchronized关键字包裹需要保护的代码区域同时指定一个锁对象。运行过程中同一时间只会有一个线程进入代码块执行以此保障操作的原子性所有竞争同一资源的线程必须使用同一个锁对象才能生效。同步方法同样依靠synchronized关键字实现直接将关键字修饰在方法上。若是普通成员方法默认以当前实例对象作为锁若是静态方法则以类的字节码对象作为锁作用效果和同步代码块保持一致。Lock锁是另一种同步方案java.util.concurrent.locks.Lock接口提供了比synchronized更加丰富的功能能够实现非阻塞获取锁、限时等待锁等效果日常开发中最常使用它的实现类ReentrantLock。编码时先调用方法获取锁执行完核心逻辑后一定要在finally代码块中释放锁避免锁资源一直被占用。3. 读写锁ReadWriteLockReentrantReadWriteLock是读写锁的核心实现类它将锁分为读锁与写锁两类。多个线程可以同时获取读锁并发执行读取操作彼此之间不会产生互斥。而写锁具备强排他性当一个线程持有写锁时其他线程无论是执行读操作还是写操作都必须等待。在读取数据频次远高于写入数据的场景中使用读写锁能够大幅提升程序并发运行效率。五、线程间通信与协作1. wait()与notify()/notifyAll()机制线程调用wait方法后会释放手中的锁资源并进入等待队列直到被其他线程唤醒或者线程被中断才会重新参与锁的竞争。notify方法会随机唤醒一名正在等待的线程notifyAll则会唤醒队列里全部的等待线程这两个方法执行后当前线程不会立刻释放锁。这套组合机制最经典的应用就是生产者与消费者模型生产者完成数据生产后唤醒消费者取数据消费者消费完毕后再唤醒生产者继续生产有效避免数据重复生产或是无数据可消费的问题。2. sleep()与wait()的核心区别sleep方法归属Thread类wait方法则定义在Object类当中。线程执行sleep时只是暂停运行不会释放已经获取的锁资源等待时间结束就会自动恢复执行该方法可以在代码任意位置调用。而线程执行wait时会主动释放锁进入持续等待状态只能依靠其他线程调用唤醒方法才能继续运行并且该方法严格要求必须写在同步代码块或者同步方法内部。六、线程池的应用与管理1. 线程池的核心优势频繁创建和销毁线程会消耗大量系统资源线程池提前初始化一批线程并统一管理实现线程对象的重复利用减少资源损耗。同时线程池可以集中管控所有线程的状态、数量简化并发代码的编写全面提升程序整体的运行效率。2. 线程池核心接口与类Executor是线程池体系的顶层接口规定了线程池最基础的执行规范。ExecutorService继承自Executor拓展出提交任务、关闭线程池等一系列实用方法也是日常使用最多的线程池接口。Executors是专门用来创建线程池的工具类依靠静态方法快速生成不同类型的线程池。固定数量线程池会维持指定个数的线程在线程池中运行适合任务数量长期稳定的场景。可缓存线程池不会限制线程数量会根据任务多少动态创建或回收线程适合执行大量短时任务。3. Callable与Future的异步处理Callable接口用于定义带有返回值的异步任务内部call方法执行完成后可以返回结果也能主动抛出异常。Future对象用来接收异步任务的运行状态与结果调用对应方法可以阻塞等待任务执行完成并获取返回数据也能单独判断任务是否已经执行结束。七、并发集合类1. 线程安全的集合实现CopyOnWriteArrayList是线程安全的列表集合它在执行写入、修改、删除操作时会复制一份新的底层数组完成操作读取操作则全程不加锁读写互不干扰在读操作居多的场景下性能表现优异。ConcurrentLinkedQueue属于无阻塞的队列集合依托CAS算法实现线程安全不会使用传统锁机制阻塞线程在高并发的数据传递场景中使用广泛。ConcurrentHashMap是常用的线程安全键值集合采用特殊的加锁机制相比早期线程安全集合并发处理能力和运行效率都有很大提升。