前言
今天来学一下,java并发变成之-基础概念篇的内容,基础概念篇分为三块内容,分别是:
- 多线程简介:解释什么是线程,为什么在Java中使用多线程,以及它如何提高应用程序的性能和响应性。
- 线程与进程的区别:澄清这两个基本概念,解释它们在操作系统中的角色以及它们之间的关系。
- Java中的线程模型:详细介绍Java平台中的线程是如何被操作系统管理和调度的
让我们更细致且优雅地探讨Java并发编程的基础概念篇章,这一学习之旅将分为三个核心章节,旨在为您的编程技能奠定坚实的基石。
第一章:多线程编程概览
1、线程
线程(Thread)是操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位。线程是独立调度和分派的基本单位,同一进程中的多条线程将共享该进程中的全部系统资源,但每条线程又有各自的调用栈、寄存器环境和线程本地存储。线程具有轻型实体、独立调度和分派的基本单位等属性。
2、在Java中使用多线程的原因
- 提高资源利用率:通过在同一个进程中运行多个线程,可以更高效地利用多核处理器的计算资源。
- 改善程序响应性:在用户界面等交互式应用程序中,多线程可以避免界面的冻结,提高用户体验。
- 简化模型:多线程提供了一种相对简单的并行计算模型,便于开发者理解和编程。
3、多线程如何提高应用程序的性能和响应性
- 并行处理:多线程允许程序同时处理多个任务,特别是在多核或多CPU的系统上,可以显著提高程序的执行吞吐率。
- 合理划分任务:通过合理划分任务,将大任务分解成多个小任务,并分配给不同的线程执行,可以充分利用多核处理器的计算能力,提高程序的并发能力。
- 避免阻塞:在多线程环境中,一个线程的执行不会阻塞其他线程的执行,这样可以确保应用程序的响应性。例如,当一个线程负责I/O处理或人机交互而被阻塞时,其他线程可以继续执行密集计算的任务。
- 利用并发容器:Java中提供了多种并发容器,如ConcurrentHashMap、ConcurrentLinkedQueue等,这些容器可以在多线程环境下高效地操作数据,避免多线程竞争导致的数据不一致和线程安全问题。
- 锁和同步机制:使用锁和同步机制可以保证多线程之间的顺序和互斥,防止线程之间的冲突,从而确保数据的一致性和程序的正确性。
- 避免死锁:通过合理设计线程之间的依赖关系和资源的请求顺序,以及使用线程池和资源分配策略,可以减少死锁的发生,确保程序的稳定运行。
第二章:线程与进程的辨析
线程与进程是现代操作系统中实现并发执行和资源管理的两个基本概念,它们既有区别也有紧密的联系。
1、线程和进程的区别
-
资源分配与管理:
- 进程是操作系统进行资源分配(如内存、I/O设备)的最小单位。每个进程都有独立的地址空间,即拥有自己的一套完整的资源,包括代码、数据、堆栈等,因此进程之间是相互隔离的。
- 线程则是在进程内部的执行单元,是CPU调度的基本单位。同一进程内的线程共享该进程的地址空间和资源,包括代码段、数据段和打开的文件等,但每个线程有自己的调用栈、程序计数器等执行上下文。
-
调度与切换开销:
- 进程之间的切换涉及到整个地址空间的切换,因此开销较大。
- 线程之间的切换只需要保存和恢复少量寄存器内容和栈信息,开销较小,因此线程间的并发执行效率通常高于进程。
-
通信与同步:
- 同一进程内的线程可以直接访问共享内存,通信简单且高效。
- 不同进程之间的通信通常需要通过进程间通信(IPC, Inter-Process Communication)机制,如管道、信号量、套接字等,相对复杂且开销较大。
-
独立性与生存期:
- 进程是独立运行的实体,一个进程的崩溃不会直接影响到其他进程。
- 线程依赖于其所属的进程,如果进程终止,其内的所有线程也将停止执行。
2、线程和进程的联系
- 包含关系:一个进程可以包含一个或多个线程,至少包含一个主线程。
- 资源共享:同一进程内的所有线程共享该进程的资源,这简化了线程间的数据交换。
- 协同工作:线程之间可以相互协作,共同完成进程的任务,通过同步机制(如互斥锁、信号量)来协调对共享资源的访问,避免数据竞争和不一致问题。
- 系统支持:无论是进程还是线程,都是操作系统提供的机制,用于实现程序的并发执行,提高系统的效率和响应速度
第三章:Java线程模型的深度探索
1. 线程的生命周期
Java线程在其存在期间会经历几种不同的状态,这些状态包括:
- 新建(New):线程被创建但尚未启动。
- 可运行(Runnable):线程可以由线程调度器安排执行,包括运行中和就绪两种子状态。
- 阻塞(Blocked):等待获取一个内部锁或其他同步资源,无法执行。
- 等待(Waiting):无限期等待另一个线程执行特定操作,例如调用
Object.wait()
。 - 计时等待(Timed Waiting):类似于等待状态,但只等待特定时间,如通过
Thread.sleep()
设定的时长。 - 终止(Terminated):线程已结束执行。
2. 线程的创建与执行
- Thread类与Runnable接口:Java提供了两种创建线程的方式,一是直接继承
Thread
类并覆盖run()
方法;二是实现Runnable
接口,并将其实例传递给Thread
类的构造函数。 - 线程调度:Java线程的调度最终依赖于底层操作系统,但JVM提供了优先级(
setPriority()
)设置,影响线程调度的倾向性,尽管实际效果受OS影响。
3. 线程同步与通信
- synchronized关键字:用于方法或代码块,实现互斥锁,确保同一时刻只有一个线程可以访问临界区。
- volatile关键字:确保变量的读写操作不会被指令重排序,并保证了多线程环境下的可见性。
- wait(), notify(), notifyAll():这些方法必须在
synchronized
块或方法内调用,用于线程间的协调与通信。
4. 高级同步机制
- java.util.concurrent.locks包:提供如
ReentrantLock
、ReadWriteLock
等高级锁机制,支持更复杂的同步需求,如公平锁、可中断锁等。 - 原子类(AtomicXxx):位于
java.util.concurrent.atomic
包中,提供了一组线程安全的原子操作类,用于在无锁情况下实现线程安全的更新操作。 - Condition接口:与Lock配合使用,作为对传统wait/notify机制的改进,提供更灵活的线程间协调能力。
5. 线程池与Executor框架
- Executor框架:从Java 5开始引入,推荐使用
ExecutorService
接口及其实现(如ThreadPoolExecutor
)来管理和控制线程,以替代传统的线程创建方式。线程池可以有效复用线程,减少线程创建销毁的开销,提高系统效率和响应速度。
6. 线程局部变量(ThreadLocal)
ThreadLocal
类允许每个线程拥有自己的变量副本,即使变量名相同,在不同线程中也不会相互影响,有助于避免线程安全问题,简化并发编程。
附录
1、java线程状态代码示例
我们可以通过Java内部提供的枚举类型Thread.State
,查看所有的Java线程状态
public class ThreadDemo16 {
public static void main(String[] args) {
for (Thread.State state : Thread.State.values()) {
System.out.println(state);
}
}
}
public class ThreadStatesExample {
public static void main(String[] args) {
Thread t = new Thread(new Runnable() {
public void run() {
System.out.println("线程正在运行...");
try {
// 线程进入阻塞状态
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 线程结束,进入死亡状态
}
});
// 新建状态
// 当调用start()方法后,线程进入就绪状态,等待被调度
t.start();
// 输出线程状态
Thread.State state = t.getState();
System.out.println("线程的状态: " + state);
// 判断线程是否活着
boolean isAlive = t.isAlive();
System.out.println("线程是否活着: " + isAlive);
}
}
NEW:表示线程处于新建状态,但是仍未开始执行任务(调用start方法之前)
RUNNABLE:表示线程处于已就绪状态,此时线程正处于CPU上调度或者可以随时被CPU调度
BLOCKED:表示当前线程由于锁竞争处于阻塞等待状态
WAITING:表示当前线程处于阻塞等待状态
TIMED_WATING:表示当前线程处于带有时限的阻塞等待状态(超时等待)
TERMINATED:表示当前线程任务完成,处于终止状态(run方法执行完毕)
2、java线程状态图解
线程状态图解说明:
- 新建(New) -> 可运行(Runnable):调用线程的
start()
方法。 - 可运行(Runnable) -> 阻塞(Blocked):请求一个锁,而该锁被其他线程持有。
- 可运行(Runnable) -> 等待(Waiting) 或 计时等待(Timed Waiting):调用特定的等待方法。
- 阻塞(Blocked) / 等待(Waiting) / 计时等待(Timed Waiting) -> 可运行(Runnable):等待的条件满足,比如锁被释放,或者等待时间到期,或者其他线程通过
notify()
/notifyAll()
或LockSupport.unpark()
唤醒。 - 可运行(Runnable) -> 终止(Terminated):线程执行完毕自然结束,或者因未捕获的异常导致提前结束