Java的线程
Java的线程实现
1.线程,进程和管程
1.1线程(Thread)
- 定义:线程是操作系统中能够独立运行的最小单位,是进程的一个执行分支。一个进程可以包含多个线程,它们共享同一进程的资源(如内存和文件句柄)。
- 特点:
- 线程之间的创建和销毁开销较小。
- 线程间共享内存,通信较为高效,但也容易引发竞争条件和数据不一致问题。
1.2进程(Process)
- 定义:进程是程序在计算机上运行的实例,它拥有自己的内存空间和资源。进程之间是相互独立的,通常通过进程间通信(IPC)进行数据交换。
- 特点:
- 进程有自己的地址空间,线程间不共享内存。
- 进程的创建和销毁开销较大,但提供更好的隔离性和稳定性。
1.3管程(Monitor)
- 定义:管程是一种高层次的同步机制,用于控制对共享资源的访问。它将共享资源的访问和管理封装在一个对象中,并提供互斥访问。
- 特点:
- 管程通常包括一个互斥锁和一些条件变量。
- 通过管程,可以避免线程间的竞争条件,简化线程同步的复杂性。
2.用户线程和守护线程
用户线程:系统的工作线程,会完成这个程序需要完成的业务操作。
守护线程:服务进程,没有服务对象就没有必要继续运行下去了,如果用户线程全部结束,意味着程序需要完成的业务操作已经结束,系统可以退出,当只剩下守护线程时,Java虚拟机会自动退出。
3.并发和并行
3.1并发(Concurrency)
- 定义:并发是指多个任务在同一时间段内进行,不一定是同时执行的。任务可能在共享的时间片上交替运行。
- 特点:
- 任务之间可以相互影响,可以在同一时间片上切换。
- 并发的实现可以通过多线程、异步编程等方式。
- 适用于需要同时处理多个任务的场景,但不一定能提高程序的执行速度。
3.2并行(Parallelism)
- 定义:并行是指多个任务在同一时刻同时执行。通常是在多核处理器上,多个任务可以同时在不同的核心上运行。
- 特点:
- 任务是独立的,可以同时进行,不需要相互干扰。
- 并行通常涉及到对任务的划分,能够有效利用多核处理器的资源。
- 适用于计算密集型任务,可以显著提高程序的执行速度。
4.创建线程的方式
4.1. 继承Thread
类
通过继承Thread
类并重写其run()
方法来创建线程。然后实例化该类并调用start()
方法启动线程。
4.2. 实现Runnable
接口
通过实现Runnable
接口并重写其run()
方法,然后将该实现传递给Thread
对象,再调用start()
方法启动线程。
5.线程的状态及转换
Java中的线程状态主要有以下几种,每种状态之间可以通过特定的操作进行转换:
5.1 新建状态(New)
- 状态描述:线程被创建但尚未启动。
- 转换:通过调用
start()
方法将线程从新建状态转换为就绪状态。
5.2 就绪状态(Runnable)
- 状态描述:线程在此状态中准备执行,但可能因为操作系统调度等原因未被执行。
- 转换:线程可能因为被调用
start()
而进入此状态,随后调度器决定何时将其转换为运行状态。
5.3 运行状态(Blocked)
- 状态描述:线程正在执行其任务。
- 转换:线程会在就绪状态中被调度到运行状态。运行状态可以因时间片耗尽或调用
yield()
等返回就绪状态。
5.4 阻塞状态(Blocked)
- 状态描述:线程因等待锁而被阻塞。
- 转换:当线程尝试获取一个已被其他线程占用的锁时,进入此状态。当锁可用时,线程会转回就绪状态。
5.5 等待状态(Waiting)
- 状态描述:线程在此状态中等待其他线程的特定动作(如
notify()
或notifyAll()
)。 - 转换:线程可以通过调用
wait()
、join()
或LockSupport.park()
进入此状态。当被其他线程唤醒时,会转回就绪状态。
5.6 超时等待状态(Timed Waiting)
- 状态描述:线程在此状态中等待一段时间。
- 转换:通过调用带有超时参数的方法(如
sleep(milliseconds)
、wait(milliseconds)
等)进入此状态。当时间到达后,线程会转回就绪状态。
5.7 终止状态(Terminated)
- 状态描述:线程已经完成执行或因异常退出。
- 转换:当
run()
方法执行完毕或线程异常抛出后,线程会进入此状态。
5.8状态转换总结
- 新建 → 就绪:调用
start()
- 就绪 → 运行:线程调度
- 运行 → 就绪:时间片耗尽、调用
yield()
- 运行 → 阻塞:等待锁
- 运行 → 等待:调用
wait()
、join()
- 等待 → 就绪:被唤醒
- 超时等待 → 就绪:时间到达
- 运行 → 终止:
run()
结束或抛出异常
6.线程的锁
在 Java 中,synchronized
加锁主要有以下几种情况:
6.1 实例方法加锁:在方法声明上使用 synchronized
关键字,锁住当前对象实例(this
)。同一时刻,只有一个线程能够执行该实例的方法。
Runnable创建线程时,使用实例方法加锁
继承Thread类创建线程时,使用实例方法加锁
6.2 静态方法加锁:在静态方法上使用 synchronized
,锁住类的 Class 对象。这意味着同一时刻,只有一个线程能够执行该类的静态方法。
6.3 代码块加锁:在需要加锁的代码块上使用 synchronized
,可以指定锁对象。锁定特定对象可以提高并发性。
继承Thread类创建线程时,使用同步代码块加锁
Runnable创建线程时,使用同步代码块加锁
6.4 类锁:对一个类的 Class 对象加锁,锁为当前类的class对象(.class),通常用于静态代码块。
Java的线程
https://blog.liuzijian.com/post/7e9cf814-2856-46b9-b30d-7a77f7090b04.html