【Java零基础连载16】Java多线程入门|线程原理、创建方式、线程状态、线程安全、锁机制详解
✨ 专栏Java零基础全套入门连载教程 简介本文承接上篇Java异常处理内容正式开启Java高并发核心——多线程篇章。多线程是Java后端进阶的重中之重也是面试高频考点、项目性能优化的核心手段。日常开发的异步处理、批量任务、定时任务、接口并发、服务器高吞吐全部依赖多线程实现。很多新手只会简单创建线程却不懂线程底层原理、生命周期状态、线程安全隐患、锁机制使用场景频繁出现数据错乱、线程死锁、程序卡死等问题。本文从零通俗易懂讲解多线程全套体系搭配海量可运行实战代码、面试必背知识点、开发避坑指南零基础彻底吃透Java多线程 标签Java多线程,线程原理,线程创建,线程状态,线程安全,Synchronized,Lock锁,Java并发一、前言前面我们学完了Java基础核心全套内容语法、集合、泛型、IO流、异常处理已经具备了基础业务编码能力。但目前我们写的所有代码都是单线程串行执行代码从上到下逐行执行上一段代码阻塞下一段代码无法执行执行效率极低。在实际项目中批量处理上万条数据接口需要同时处理多个用户请求异步发送短信、日志记录、消息推送后台定时任务、文件异步解析单线程完全无法满足性能需求多线程技术就是为了解决程序并发执行、提升资源利用率、提高系统吞吐量而生。多线程是Java进阶后端、学习NIO、Netty、SpringBoot异步、分布式并发的必备前置基础也是面试必考重难点读完本篇你将彻底掌握进程与线程核心区别、多线程底层原理与优势Java四种线程创建方式、优缺点与实战场景线程常用API、线程优先级、守护线程、线程休眠线程七大生命周期状态、状态切换流程面试必背线程安全问题产生原因、数据错乱实战演示同步代码块、同步方法、Lock显式锁实战线程死锁成因、规避方案与企业级最佳实践二、进程与线程核心概念2.1 什么是进程进程Process操作系统资源分配的最小单位是独立运行的程序。例如电脑运行的浏览器、微信、IDEA、Java程序每一个独立程序都是一个进程。特点进程之间相互独立拥有独立的内存空间互不干扰。2.2 什么是线程线程Thread进程中的执行单元是CPU调度的最小单位隶属于进程。一个进程可以包含多个线程多个线程共享进程内存资源实现并发执行。2.3 进程与线程核心区别面试必背资源归属进程独立占有资源线程共享进程资源开销大小进程创建销毁开销极大线程轻量级、开销极小独立性进程相互独立一个进程崩溃不影响其他进程线程共享资源一个线程异常可能导致整个进程崩溃执行效率线程并发执行效率远高于进程2.4 多线程核心优势充分利用CPU多核资源避免CPU空闲浪费异步执行任务提升程序响应速度与吞吐量任务并发处理大幅提升批量业务执行效率三、Java四种线程创建方式全覆盖实战Java提供四种正规的线程创建方式不同方式适配不同业务场景开发中按需选择。3.1 方式一继承Thread类基础入门自定义类继承Thread类重写run()方法调用start()启动线程。// 1.自定义线程类继承Thread public class MyThread extends Thread { // 重写run方法线程执行的核心任务 Override public void run() { for (int i 0; i 10; i) { System.out.println(线程执行 i); } } public static void main(String[] args) { // 2.创建线程对象 MyThread thread new MyThread(); // 3.启动线程JVM自动调用run方法 thread.start(); } }注意必须调用start()启动线程直接调用run()只是普通方法执行不会开启新线程。缺点单继承限制继承Thread后无法再继承其他类扩展性差。3.2 方式二实现Runnable接口开发常用实现Runnable接口重写run方法解决单继承限制推荐优先使用。// 1.实现Runnable任务接口 public class MyRunnable implements Runnable { Override public void run() { for (int i 0; i 10; i) { System.out.println(Runnable线程执行 i); } } public static void main(String[] args) { // 2.创建任务对象 MyRunnable runnable new MyRunnable(); // 3.线程对象绑定任务启动线程 new Thread(runnable).start(); } }优点接口多实现、无继承限制、任务与线程解耦适合资源共享场景。3.3 方式三Callable FutureTask带返回值、可抛异常前两种方式线程无返回值、无法抛出异常Callable可以获取线程执行结果支持异常捕获。import java.util.concurrent.Callable; import java.util.concurrent.FutureTask; // 1.实现Callable接口指定返回值泛型 public class MyCallable implements CallableInteger { Override public Integer call() throws Exception { // 线程任务有返回值 int sum 0; for (int i 1; i 10; i) { sum i; } return sum; } public static void main(String[] args) throws Exception { MyCallable callable new MyCallable(); // 包装任务 FutureTaskInteger futureTask new FutureTask(callable); // 启动线程 new Thread(futureTask).start(); // 获取线程执行返回结果阻塞等待 Integer result futureTask.get(); System.out.println(线程执行结果 result); } }适用场景需要线程执行结果、需要捕获线程内部异常的业务场景。3.4 方式四线程池创建企业开发首选频繁创建销毁线程消耗系统资源线程池可以复用线程、控制并发数、统一管理线程正规项目全部使用线程池。import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; public class ThreadPoolDemo { public static void main(String[] args) { // 创建固定大小线程池 ExecutorService pool Executors.newFixedThreadPool(3); // 提交任务 for (int i 0; i 5; i) { pool.execute(() - { System.out.println(线程池任务执行 Thread.currentThread().getName()); }); } // 关闭线程池 pool.shutdown(); } }四、线程常用核心APIpublic class ThreadApiDemo { public static void main(String[] args) throws InterruptedException { // 获取当前线程对象 Thread mainThread Thread.currentThread(); // 获取线程名称 System.out.println(主线程名称 mainThread.getName()); // 设置线程名称 mainThread.setName(主线程); // 线程休眠毫秒暂停当前线程 Thread.sleep(1000); // 设置线程优先级 1-10优先级越高获取CPU时间片概率越大 mainThread.setPriority(10); // 守护线程主线程退出守护线程自动退出 // thread.setDaemon(true); } }核心API说明Thread.currentThread()获取当前执行线程sleep(long time)线程休眠释放CPU时间片不释放锁setPriority()设置线程优先级仅提高执行概率不绝对优先setDaemon(true)设置为守护线程伴随主线程存活join()线程插队等待该线程执行完毕再执行其他线程五、线程七大生命周期面试必考Java线程在运行过程中会经历七大状态切换是面试高频考点必须熟记状态流转流程。5.1 七大线程状态NEW新建线程对象创建未调用start()未启动RUNNABLE就绪/运行调用start()后等待CPU调度或正在执行BLOCKED阻塞等待获取锁资源未拿到锁处于阻塞等待WAITING无限等待wait()、join()触发无限等待需手动唤醒TIMED_WAITING限时等待sleep(time)、wait(time)限时等待自动唤醒TERMINATED终止线程任务执行完毕或异常终止线程死亡5.2 核心状态流转逻辑新建 → 启动就绪 → CPU调度运行 → 阻塞/等待 → 唤醒就绪 → 运行 → 任务结束终止六、线程安全问题详解核心重难点6.1 什么是线程安全问题多线程同时操作共享资源由于CPU切换执行导致数据读写错乱、数据覆盖、结果异常就是线程不安全。6.2 线程不安全实战演示public class ThreadUnSafeDemo { // 共享资源 private static int count 0; public static void main(String[] args) { // 两个线程同时对变量自增10000次 new Thread(() - { for (int i 0; i 10000; i) { count; } }).start(); new Thread(() - { for (int i 0; i 10000; i) { count; } }).start(); // 等待线程执行完毕 try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } // 预期结果20000实际结果永远小于20000 System.out.println(最终统计结果 count); } }问题原因count 分为读取、运算、赋值三步多线程交替执行导致覆盖写入数据丢失。6.3 线程安全三大解决方案同步代码块局部加锁锁粒度最小性能最优同步方法方法整体加锁简单易用Lock显式锁JDK1.5后可灵活加锁解锁功能更强七、锁机制实战解决线程安全7.1 Synchronized 同步代码块推荐锁定共享资源仅锁住核心代码性能最优。public class SyncBlockDemo { private static int count 0; // 锁对象 private static final Object LOCK new Object(); public static void main(String[] args) throws InterruptedException { new Thread(() - { for (int i 0; i 10000; i) { // 同步代码块保证同一时间只有一个线程执行 synchronized (LOCK) { count; } } }).start(); new Thread(() - { for (int i 0; i 10000; i) { synchronized (LOCK) { count; } } }).start(); Thread.sleep(2000); // 结果稳定20000线程安全 System.out.println(最终统计结果 count); } }7.2 Synchronized 同步方法public class SyncMethodDemo { private static int count 0; // 同步方法静态方法锁类对象 public static synchronized void addCount() { count; } public static void main(String[] args) throws InterruptedException { new Thread(() - { for (int i 0; i 10000; i) { addCount(); } }).start(); new Thread(() - { for (int i 0; i 10000; i) { addCount(); } }).start(); Thread.sleep(2000); System.out.println(最终统计结果 count); } }7.3 Lock 显式锁JDK1.5 高性能锁手动加锁、手动解锁可中断、可超时、更灵活高并发场景首选。import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; public class LockDemo { private static int count 0; // 创建可重入锁 private static final Lock LOCK new ReentrantLock(); public static void main(String[] args) throws InterruptedException { new Thread(() - { for (int i 0; i 10000; i) { LOCK.lock(); // 手动加锁 try { count; } finally { LOCK.unlock(); // 手动解锁保证锁一定释放 } } }).start(); new Thread(() - { for (int i 0; i 10000; i) { LOCK.lock(); try { count; } finally { LOCK.unlock(); } } }).start(); Thread.sleep(2000); System.out.println(最终统计结果 count); } }7.4 Synchronized 与 Lock 核心区别Synchronized隐式锁自动加锁解锁使用简单适合低并发Lock显式锁手动控制可重入、可中断、可超时高并发性能更好八、线程死锁成因与规避8.1 什么是死锁多线程互相持有对方需要的锁同时等待对方释放锁双方无限等待程序卡死无法继续执行。8.2 死锁产生四大必要条件必背互斥条件锁资源同一时间只能被一个线程持有请求保持条件线程持有当前锁同时请求新锁不可剥夺条件锁只能主动释放无法被强行抢占循环等待条件线程之间形成循环锁等待链路8.3 死锁规避方案统一锁的获取顺序避免循环等待缩小锁粒度减少锁嵌套使用Lock可超时锁避免永久阻塞九、本篇总结本篇彻底攻克Java多线程入门全套核心知识打通Java并发编程基础壁垒理解进程线程本质区别、多线程并发优势与应用场景熟练掌握四种线程创建方式适配不同业务场景选型掌握线程常用API、优先级、守护线程、线程插队等特性熟记线程七大生命周期、状态流转核心逻辑吃透面试考点理解线程安全问题成因掌握三种锁解决方案精通Synchronized与Lock锁的使用与差异规范加锁编码掌握死锁四大条件与规避方案杜绝线上卡死问题多线程是Java后端进阶的核心分水岭学好本篇内容可为后续线程池、JUC并发包、高并发调优、Netty网络编程打下坚实基础十、下期预告下一篇Java线程池精讲核心参数、七大线程池、拒绝策略、线程池优化实战深入企业开发必备线程池技术详解线程池核心原理、核心参数含义、内置七大线程池特性、四种拒绝策略、手写自定义线程池、线程池参数调优彻底告别手动创建线程掌握高并发项目标准开发规范持续更新Java零基础全套连载关注专栏从零学Java稳步进阶后端开发