一、线程的状态
在Java中,线程在创建并启动后,不是一开始就进入执行状态,也不是一直处于执行状态。在线程的生命周期中,它要经过新建(NEW)、就绪(RUNNABLE)、阻塞(BLOCKED)、等待(WAITING)、超时等待(TIME_WAITING)、终止(TERMINATED)六种状态。线程运行后,CPU会在多条线程间切换,于是线程状态也会多次在运行和阻塞间切换。
线程状态枚举源码(点击查看代码)
public enum State {
/**
* Thread state for a thread which has not yet started.
*/
NEW,
/**
* Thread state for a runnable thread. A thread in the runnable
* state is executing in the Java virtual machine but it may
* be waiting for other resources from the operating system
* such as processor.
*/
RUNNABLE,
/**
* Thread state for a thread blocked waiting for a monitor lock.
* A thread in the blocked state is waiting for a monitor lock
* to enter a synchronized block/method or
* reenter a synchronized block/method after calling
* {@link Object#wait() Object.wait}.
*/
BLOCKED,
/**
* Thread state for a waiting thread.
* A thread is in the waiting state due to calling one of the
* following methods:
* <ul>
* <li>{@link Object#wait() Object.wait} with no timeout</li>
* <li>{@link #join() Thread.join} with no timeout</li>
* <li>{@link LockSupport#park() LockSupport.park}</li>
* </ul>
*
* <p>A thread in the waiting state is waiting for another thread to
* perform a particular action.
*
* For example, a thread that has called <tt>Object.wait()</tt>
* on an object is waiting for another thread to call
* <tt>Object.notify()</tt> or <tt>Object.notifyAll()</tt> on
* that object. A thread that has called <tt>Thread.join()</tt>
* is waiting for a specified thread to terminate.
*/
WAITING,
/**
* Thread state for a waiting thread with a specified waiting time.
* A thread is in the timed waiting state due to calling one of
* the following methods with a specified positive waiting time:
* <ul>
* <li>{@link #sleep Thread.sleep}</li>
* <li>{@link Object#wait(long) Object.wait} with timeout</li>
* <li>{@link #join(long) Thread.join} with timeout</li>
* <li>{@link LockSupport#parkNanos LockSupport.parkNanos}</li>
* <li>{@link LockSupport#parkUntil LockSupport.parkUntil}</li>
* </ul>
*/
TIMED_WAITING,
/**
* Thread state for a terminated thread.
* The thread has completed execution.
*/
TERMINATED;
}
1、新建状态(NEW)
当使用new
关键字创建一个新的线程时,线程就处于新建状态(NEW)了。此时JVM为其分配内存,并初始化成员变量的值。
2、就绪状态(RUNNABLE)
当线程对象调用start()
方法时,该线程就处于就绪状态(RUNNABLE)。JVM会为其创建方法调用栈和程序计数器,等待调用运行。
运行状态(RUNNING) 如果就绪线程获得了CPU执行,开始执行run()
方法的线程执行体,则该线程处于运行状态(RUNNING),实际在Java源码中并没有维护该状态,它被包含在就绪状态中。
3、阻塞状态(BLOCKED)
阻塞状态(BLOCKED)是指线程由于某种原因放弃了CPU使用权,暂时停止运行。知道线程进入就绪状态(RUNNABLE),才有机会再次获得CPU使用权转到运行状态(RUNNING)。
阻塞状态分三种:
- 等待阻塞(o.wait()->等待队列)
运行状态的线程执行o.wait()
方法,JVM会把该线程放入等待队列中。 - 同步阻塞(lock->锁池)
运行状态的线程在获取对象的同步锁时,若该同步锁在被别的线程使用,那么JVM会把该线程放入锁池中(lock pool)。 - 其他阻塞(sleep/join)
运行状态的线程执行Thread.sleep(long ms)
、t.join()
方法,或发出了I/O请求时,JVM会把该线程置为阻塞状态。当sleep()
超时、join()
等待线程终止或超时、I/O请求处理完毕时,线程重新转入就绪状态(RUNNABLE)
4、等待(WAITING)
等待状态,没有超时时间(无限等待),需要被其他线程或者其他的中断操作。
执行o.wait()
、t.join()
、LockSupport.park()
5、超时等待(TIME_WAITING)
与等待不同的是,不是无限等待,超时后自动返回
执行t.sleep()
,带参数的o.wait(long)
等可以实现
6、终止状态(TERMINATED)
线程会已以下三种方式结束,结束后就是终止状态。
-
正常结束:
run()
或call()
方法执行完成,线程正常结束。 -
异常结束:
线程抛出一个未捕获的Exception
或Error
. -
调用stop()方法:
直接调用线程的stop()
方法结束线程。该方法通常容易导致死锁,不推荐使用。
二、线程基本方法
1、start()
Thread通过start()
方法来启动一个线程,这时此线程处于就绪状态,并没有马上运行。
2、run()
run()
方法为线程体,它包含了该线程要执行的内容。当线程进入运行状态时,开始执行run()
方法中的代码。run()
方法代码执行结束,此线程终止。
3、wait()
wait()
方法是Object
类中的方法。调用该方法的线程进入WAITING状态,只有等待其他线程通知或被中断才会返回。
调用wait()
方法后,会释放对象锁,进入等待锁定池,只有针对该对象调用notify()方法后本线程才进入对象锁定池准备获取对象锁进入运行状态。因此,wait()
方法一般被用在同步方法或同步代码块中。
4、sleep(long)
sleep(long)
方法是Thread
类中的方法。sleep(long)
方法导致线程进入休眠,进入TIME_WAITING状态,与wait()
方法不同的是,sleep(long)
不会释放对象锁。
sleep(long)
方法会让线程暂停执行指定的时间,让出CPU资源,但是它的监控状态会一直保持,当指定的时间到了会自动恢复就绪状态。
5、yield()
线程让步。 yield()方法会使当前线程让出CPU使用权,让其他线程重新竞争CPU使用权。一般情况下,优先级高的线程有更高的概率成功竞争到CPU使用权,但这不是绝对的,有的操作系统对线程优先级并不敏感。
6、interrupt()
线程中断。 中断一个线程,其本意是给这个线程一个通知信号,会影响这个线程内部的一个中断标识位。这个线程本身并不会因此而改变状态(如阻塞、终止等)。
- 调用
interrupt()
方法并不会中断一个线程。运行中的线程并不会因此而终止,只是改变了线程内部维护的中断标识位而已。 - 若调用
sleep(long)
使线程进入TIMED_WAITING状态,此时调用interrupt()
方法,会抛出InterruptedException
异常,使线程提前结束TIMED_WAITING状态。 - 许多申明抛出
InterruptedException
异常的方法,抛出异常前,都会清楚中断标识位,所以抛出异常后,调用isInterrupted()
方法将会返回false
. - 中断状态是线程固有的一个标识位,可以通过此标识位安全的终止线程。通过调用
interrupt()
方法,在run()
方法内可以判断t.isInterrupted()
的值来优雅的终止线程。
7、join()
等待其他线程终止。 在当前线程中调用一个线程的join()
方法,则当前线程转为阻塞状态,等到调用join()
方法的线程终止,当前线程再由阻塞状态转为就绪状态。
很多情况下,主线程创建了子线程,需要用到子线程的返回结果,也就是主线程需要在子线程结束之后再结束,这时就需要用到join()
方法。
System.out.println("主线程执行开始");
MyThread myThread = new MyThread();
myThread.start();
myThread.join();
System.out.println("主线程执行结束");
结果必然为:
主线程执行开始
MyThread running
主线程执行结束
8、notify()
线程唤醒。 notify()
是Object
类中的方法,唤醒在此对象监视器上等待的单个线程,如果有多个线程都在该对象上等待,则会选择唤醒其中一个线程,选择是任意的,并在对实现做出决定时发生。类似的方法还有notifyAll()
,唤醒在此监视器上的所有线程。
三、终止线程的方式
1、正常运行结束
程序运行结束,线程自动终止。
2、使用退出标志终止线程
某些线程需要长时间运行,只有满足某些条件的情况下,才能终止这些线程。可以使用一个变量来控制循环。
// 使用 volatile 关键字,保障同一时刻只能有一个线程来修改它的值
public class MyThread extends Thread{
public volatile boolean exit = false;
@Override
public void run(){
while(!exit){
System.out.println("do something");
}
}
}
3、Interrupt方法终止线程
使用interrupt()
方法来终止线程,有两种情况:
-
1、线程处于阻塞状态。 如使用了sleep、同步锁的wait,socket中的receiver、accept等方法时会使线程处于阻塞状态。当调用线程的
interrupt()
方法时,会抛出InterruptedException
异常。通过代码捕获该异常然后break跳出循环状态,从而结束这个线程的运行。一定要先捕获异常之后通过break来跳出循环,才能正常结束run方法。 -
2、线程未处于阻塞状态。 使用
isInterrupted()
判断线程的中断标志来退出循环。当使用interrupt()
方法时,中断标志就会置 true,和使用自定义的标志来控制循环是一样的道理。
public class MyThread extends Thread{
@Override
public void run(){
// 非阻塞过程中通过判断中断标志来退出
while(!isInterrupted()){
System.out.println("do something");
try {
// 阻塞过程中通过捕获异常来退出
Thread.sleep(500);
}catch (InterruptedException e){
e.printStackTrace();
break;
}
}
}
}
4、stop方法终止线程(线程不安全)
线程中可以直接使用t.stop()
方法来强行终止线程。
不安全原因:当调用stop()
方法后,创建子线程的线程就会抛出ThreadDeath
的Error错误,并且会释放子线程所持有的所以锁。一般加锁的模块都是为了保护数据的一致性,调用stop()
方法后,该线程持有的锁突然被释放,那么被保护的数据就有可能出现数据不一致的情况,其他线程再使用这些被破坏的数据时就会出现问题。因此,不推荐使用stop()
。
四、守护线程
1、定义: 守护线程是指在程序运行的时候在后台提供一种通用服务的线程,比如垃圾回收线程就是一个很称职的守护者,并且这种线程并不属于程序中不可或缺的部分。因此,当所有的非守护线程结束时,程序也就终止了,同时会杀死进程中的所有守护线程。反过来说,只要任何非守护线程还在运行,程序就不会终止。
2、设置: 可以通过setDaemon(true)
来设置线程为守护线程。
setDaemon(true)
方法必须要在start()
方法之前调用,否则会抛出IllegalThreadStateException
异常。不能把正常运行的用户线程设置为守护线程。- 在守护线程中产生的新线程也是守护线程。
- 守护线程应该永远不去访问固有资源,如文件、数据库,因为它会在任何时候甚至在一个操作的中间发生中断。
3、生命周期: 守护进程(Daemon)是运行在后台的一种特殊进程。它独立于控制终端并且周期性地执行某种任务或等待处理某些发生的事件。也就是说守护线程不依赖于终端,但是依赖于系统,与系统“同生共死”。当 JVM 中所有的线程都是守护线程的时候,JVM 就可以退出了;如果还有一个或以上的非守护线程则 JVM 不会退出。
标签:状态,Thread,thread,--,线程,多线程,方法,wait From: https://www.cnblogs.com/code-tong/p/17154857.html