Java线程控制方法的作用及其区别:
- start:
- 作用:启动线程,由虚拟机自动调度执行run()方法。
- 区别:线程处于就绪状态。
- run:
- 作用:线程逻辑代码块处理,JVM调度执行。
- 区别:线程处于运行状态。
- sleep:
- 作用:让当前正在执行的线程休眠(暂停执行)。
- 区别:不释放锁。
- wait:
- 作用:使得当前线程等待。
- 区别:释放同步锁。
- notify:
- 作用:唤醒在此对象监视器上等待的单个线程。
- 区别:唤醒单个线程。
- notifyAll:
- 作用:唤醒在此对象监视器上等待的所有线程。
- 区别:唤醒多个线程。
- yield:
- 作用:停止当前线程,让同等优先权的线程运行。
- 区别:用Thread类调用。
- join:
- 作用:使当前线程停下来等待,直至另一个调用join方法的线程终止。
- 区别:用线程对象调用。
总结:
start
和run
方法用于启动和执行线程,但它们的区别在于线程的状态不同。sleep
,wait
,notify
,notifyAll
,yield
, 和join
等方法则用于控制线程的执行顺序和状态,包括暂停、等待、唤醒和其他线程交互操作。
- 从创建到就绪状态
public class ThreadExample {
public static void main(String[] args) {
Thread myThread = new MyThread();
myThread.start();// 调用start方法,线程从创建变为就绪
}
}
class MyThread extends Thread {
public void run() {
System.out.println("线程正在运行");
}
}
- 当调用线程对象的
start()
方法时,线程就从创建状态转换为就绪状态。这是线程生命周期中的第一次状态转换。例如,在Java中: - 这里
myThread
在new
之后处于创建状态,当执行start()
方法后就进入就绪状态,等待CPU分配时间片来执行run()
方法中的内容。
- 从就绪到运行状态
- 当CPU的调度器决定将CPU时间片分配给某个就绪状态的线程时,该线程就从就绪状态转换为运行状态。这个过程是由操作系统的线程调度算法控制的。
- 例如,在一个多线程环境中,假设有线程A、B、C都处于就绪状态。操作系统的调度算法(如时间片轮转、优先级调度等)会根据一定的规则选择一个线程,比如选择了线程A,那么线程A就从就绪状态转换为运行状态,开始执行它的
run()
方法中的代码。
- 从运行到就绪状态
- 当正在运行的线程的时间片用完,或者有更高优先级的线程进入就绪状态并抢占CPU时,当前运行的线程就会从运行状态转换为就绪状态。
- 例如,采用时间片轮转调度算法,假设一个线程的时间片是10ms,当这个线程运行了10ms后,CPU会暂停该线程的执行,将其放回就绪队列,等待下一次分配时间片。再比如,一个低优先级的线程正在运行,突然有一个高优先级的线程进入就绪状态,操作系统可能会暂停低优先级线程的运行,将其转换为就绪状态,而让高优先级线程开始运行。
- 从运行到阻塞状态
- 当线程在运行过程中遇到某些阻塞事件时,会从运行状态转换为阻塞状态。如前所述,常见的阻塞事件包括等待I/O操作完成和等待获取锁。
- 例如,一个线程在运行时需要从文件中读取数据,当它调用文件读取操作(如
BufferedReader.readLine()
方法)后,由于文件读取操作可能需要一些时间(取决于文件系统、网络等因素),线程就会进入阻塞状态,等待数据读取完成。或者当多个线程竞争一个对象锁时,一个线程在运行中尝试获取被其他线程占用的锁,就会进入阻塞状态,直到锁被释放。
- 从阻塞到就绪状态
- 当导致线程阻塞的事件结束后,线程会从阻塞状态转换为就绪状态。例如,当线程等待的I/O操作完成后,或者线程成功获取到之前等待的锁时,它就会从阻塞状态转换为就绪状态。
- 比如,前面提到的读取文件数据的线程,当文件数据读取完成后,线程就会从阻塞状态转换为就绪状态,等待CPU分配时间片再次运行。对于等待锁的线程,当占用锁的线程释放锁后,等待的线程就会从阻塞状态转换为就绪状态。
- 从运行到死亡状态
- 当线程正常执行完
run()
方法中的所有代码或者因为未捕获的异常而退出run()
方法时,线程就从运行状态转换为死亡状态。 - 例如,一个线程的任务是计算1到10的和,在
run()
方法中通过循环完成计算后,run()
方法执行完毕,线程就进入死亡状态。如果线程在运行过程中抛出一个未捕获的异常,如NullPointerException
,并且没有在run()
方法中进行处理,那么线程也会进入死亡状态。