Java 多线程的原理涉及计算机科学中操作系统、并发编程和内存管理的基本概念。在 Java 中,多线程的实现依赖于操作系统的线程管理机制,以及 Java 虚拟机(JVM)的调度。
1. 线程的概念
线程是程序执行的最小单位,属于进程的一个子集。一个进程可以包含多个线程,每个线程执行不同的任务,但它们共享相同的进程资源(如内存空间、文件句柄等)。
2. Java 中的线程
Java 提供了三种方式来创建线程:
- 继承
Thread
类:通过继承Thread
类,并重写run()
方法来定义线程的执行逻辑。 - 实现 Runnable接口:通过实现
Runnable
接口的run()
方法,然后将其传递给Thread
对象的构造器来创建线程。 - 实现 Callable 接口:通过实现 Callable接口的 call
()
方法,通过FutureTask封装,然后将其传递给Thread
对象的构造器来创建线程。//实现Callable接口 class FDWW implements Callable<String>{ @Override public String call() throws Exception { System.out.println("sda"); return "success"; } } public static void main(String[] args) { //用FutureTask封装,再传递给Thread FutureTask<String> fdwwFutureTask = new FutureTask<>(new FDWW()); Thread thread = new Thread(fdwwFutureTask); thread.start(); //获取返回值 String result = fdwwFutureTask.get(); System.out.println(result); }
3. 线程的生命周期
线程在 Java 中有以下5个状态:
- 新建(New):线程对象被创建,但尚未启动。
- 就绪(Runnable):线程已经启动,等待 CPU 分配执行时间。
- 运行(Running):线程获取了 CPU 时间片,正在执行。
- 阻塞(Blocked/Waiting):线程因等待资源或信号(如锁、I/O 操作等)而被暂停执行。
- 终止(Terminated):线程的
run()
方法执行完毕,或因异常退出,线程终止。
4. 线程同步
在多线程环境中,当多个线程共享资源时,可能会导致数据不一致的问题。为了避免这种情况,Java 提供了多种同步机制:
- 同步方法和同步块:使用
synchronized
关键字,确保某个线程在执行代码块时,其它线程无法进入相同的代码块。 - 显式锁(
ReentrantLock
):Java 提供了更灵活的锁机制,例如ReentrantLock
,它提供了比synchronized
更加细粒度的控制。
5. 线程通信
Java 提供了几种线程之间通信的方式:
wait()
、notify()
和notifyAll()
:这些方法可以用于线程间的协作,wait()
使线程等待,notify()
唤醒一个等待线程,notifyAll()
唤醒所有等待线程。Condition
对象:与ReentrantLock
一起使用,提供类似wait/notify
的功能,但更为灵活。
6. 线程调度
Java 的线程调度由 JVM 和底层操作系统共同决定。调度策略主要包括:
- 时间片轮转:各个线程按时间片轮流执行。
- 优先级调度:线程根据其优先级获得不同的执行机会。
Java 提供的 Thread
类有一个 setPriority()
方法,可以调整线程的优先级,但最终执行顺序依赖于 JVM 的实现和操作系统的调度策略。
7. 线程池
线程池是 Java 中常用的线程管理机制,Executor 框架提供了丰富的线程池实现,能够有效管理和重用线程资源,避免频繁创建和销毁线程带来的开销。
创建线程池有7种方式(Executors 和 ThreadPoolExecutor):
1、Executors.newFixedThreadPool:创建一个固定大小的线程池,可控制并发的线程数,超出的线程会在队列中等待;
2、Executors.newCachedThreadPool:创建一个可缓存的线程池,若线程数超过处理所需,缓存一段时间后会回收,若线程数不够,则新建线程;
3、Executors.newSingleThreadExecutor:创建单个线程数的线程池,它可以保证先进先出的执行顺序;
4、Executors.newScheduledThreadPool:创建一个可以执行延迟任务的线程池;
5、Executors.newSingleThreadScheduledExecutor:创建一个单线程的可以执行延迟任务的线程池;
6、Executors.newWorkStealingPool:创建一个抢占式执行的线程池(任务执行顺序不确定);
7、ThreadPoolExecutor:最原始的创建线程池的方式,它包含了 7 个参数可供设置。
代码示例:
public static void main(String[] args) {
//固定大小的线程池
ExecutorService executorService = Executors.newFixedThreadPool(3);
//可缓存的线程池
ExecutorService executorService1 = Executors.newCachedThreadPool();
//单例线程池
ExecutorService executorService2 = Executors.newSingleThreadExecutor();
//抢占式线程池
ExecutorService executorService3 = Executors.newWorkStealingPool();
//定时任务线程池
ScheduledExecutorService scheduledExecutorService1 = Executors.newScheduledThreadPool(3);
//单例定时任务线程池
ScheduledExecutorService scheduledExecutorService2 = Executors.newSingleThreadScheduledExecutor();
//原生方式创建线程池,推荐
ExecutorService executorService4 = new ThreadPoolExecutor(
5,//核心线程数
10,//最大线程数
3000,//等待超时时间
TimeUnit.SECONDS,//时间单位
new ArrayBlockingQueue<>(10),//等待队列
new ThreadFactory() {//线程工厂
@Override
public Thread newThread(@NotNull Runnable r) {
return new Thread(r);
}
},
new ThreadPoolExecutor.AbortPolicy()
//拒绝策略,(共四种 1直接拒绝并抛异常、2直接拒绝不抛异常、3丢弃最老的、4由提交任务的当前线程处理,还可自定义拒绝策略)
);
}
8. JMM内存模型
Java 内存模型(Java Memory Model,JMM)定义了多线程环境下的共享变量如何在线程之间可见以及如何同步。这是理解线程安全性和并发编程的关键。
Java 多线程编程需要理解线程的生命周期、同步机制、线程通信以及 JVM 的内存模型等核心概念。掌握这些原理能够帮助开发者编写高效且安全的多线程程序。