Java多线程
单纯明快
进程与线程
进程(Process):
- 进程是程序的运行实例。它拥有独立的内存空间,系统为每个进程分配资源(如CPU时间、内存等)。多个进程之间是相互独立的,它们通常不能直接访问彼此的内存。
线程(Thread):
- 线程是进程中的执行单元,一个进程可以包含多个线程。这些线程共享同一个进程的资源(如内存),但可以独立运行,执行不同的任务。相比创建进程,创建线程的开销更小,执行效率更高。
线程的生命周期
新建状态:
- 使用new关键字和Thread类或其子类建立一个线程对象后,该线程对象就处于新建状态。它保持这个状态直到
start()
这个线程
- 使用new关键字和Thread类或其子类建立一个线程对象后,该线程对象就处于新建状态。它保持这个状态直到
就绪状态:
- 当线程对象调用了
start()
方法后,该线程就进入就绪状态。就绪状态的线程处于就绪队列中,要等待JVM里的线程调度器的调度。
- 当线程对象调用了
运行状态:
- 如果就绪状态的线程获取了CPU资源,就可以执行
run()
,此时线程便处于运行状态。处于运行状态可以变为阻塞状态,就绪状态和死亡状态。 注:run()方法会由JVM自动调用
- 如果就绪状态的线程获取了CPU资源,就可以执行
阻塞状态:
- 如果一个线程执行了sleep(睡眠)、suspend(挂起)等方法,失去所占用资源之后,该线程就从运行状态进入阻塞状态。在睡眠时间已到或获得设备资源后可以重新进入就绪状态。分为三种情况:
- 等待阻塞:运行状态中的线程自己执行了wait()方法,使线程进入等待阻塞
- 同步阻塞:线程在获取synchronized 同步锁失败(因为被其他线程占用)
- 其他阻塞:通过调用线程的sleep()或join()发出了I/O请求,线程就会进入阻塞状态。当sleep()状态超时,join()等待线程终止或超时,或者I/O处理完毕,线程重新转入就绪状态。
sleep(),wait(),join()等方法都是线程自己调用,让自己进入阻塞状态的方法。
- 如果一个线程执行了sleep(睡眠)、suspend(挂起)等方法,失去所占用资源之后,该线程就从运行状态进入阻塞状态。在睡眠时间已到或获得设备资源后可以重新进入就绪状态。分为三种情况:
死亡状态:
- 一个运行状态的线程完成任务或者其他终止条件发送时,该线程就切换到终止状态。
创建一个线程
创建线程的三种方式:
- 实现Runnable接口
- 继承Thread类
- 通过Callable 和Future 创建线程
实现Runnable接口来创建线程
正如上面我们所提到的,当线程对象调用了 start()
方法后,jvm会在合适的时机自动的调用 run()
方法作为线程运行的内容。所以继承 Runnable
接口后我们还需要重写 run()
方法来实现线程的运行逻辑。
步骤:
- 继承Runnable接口
- 重写
run()
方法 - 创建
Thread()
对象 - 调用
Thread
对象的start()
方法
1 |
|
运行结果:
1 |
|
继承Thread来创建线程
步骤:
- 创建一个类并继承Thread类
- 覆盖Thread类中的run()方法。run()方法包含了线程要执行的代码
- 创建你自定义的Tread子类的实例
- 调用该实例的start()方法来启动线程。同样,要调用start()而不是直接调用run()
1 |
|
Thread 的常用方法
对象方法:
序号 | 方法描述 |
---|---|
1 | public void start() 使该线程开始执行;Java 虚拟机调用该线程的 run 方法。 |
2 | public void run() 如果该线程是使用独立的 Runnable 运行对象构造的,则调用该 Runnable 对象的 run 方法;否则,该方法不执行任何操作并返回。 |
3 | public final void setName(String name) 改变线程名称,使之与参数 name 相同。 |
4 | public final void setPriority(int priority) 更改线程的优先级。 |
5 | public final void setDaemon(boolean on) 将该线程标记为守护线程或用户线程。 |
6 | public final void join(long millisec) 等待该线程终止的时间最长为 millisec 毫秒。不输入参数会一直等待到子线程结束。 |
7 | public void interrupt() 中断线程。 |
8 | public final boolean isAlive() 测试线程是否处于活动状态。 |
守护线程/用户线程:主线程结束后自动终止。
非守护线程:主线程结束后,会继续执行自己的任务,直到代码完毕
类方法:
序号 | 方法描述 |
---|---|
1 | public static void yield() 暂停当前正在执行的线程对象,并执行其他线程。 |
2 | public static void sleep(long millisec) 在指定的毫秒数内让当前调用该方法的线程休眠(暂停执行),此操作受到系统计时器和调度程序精度和准确性的影响。 |
3 | public static boolean holdsLock(Object x) 当且仅当当前线程在指定的对象上保持监视器锁时,才返回 true。 |
4 | public static Thread currentThread() 返回对当前正在执行的线程对象的引用。 |
5 | public static void dumpStack() 将当前线程的堆栈跟踪打印至标准错误流。 |
Thread 实例启动的线程结束后,对象实例依然存在,它的属性也不会因为线程结束而被清除。线程的终止只是意味着该线程的执行体已完成,但不代表Thread对象本身会被销毁。我们也可以通过Thread实例的属性来传递线程计算的结果。
通过Callable 和 Futrue 创建线程
实现步骤:
- 创建
Callable
接口的实现类,并实现call()
方法,该call()
方法将作为线程执行体,并且有返回值。 - 使用 FutureTask 对象作为Thread 对象的target创建并启动新线程。
- 调用FutureTask 对象的get()方法来获得子线程执行结束后的返回值。
示例:
1 |
|
Future 对象的get()方法会阻塞当前线程,直到子线程返回结果。
线程同步
Java多线程
http://blog.ulna520.com/2025/06/02/Java多线程_20250602_114227/