多线程基础(4)-线程的状态和转化的方法
4.1 操作系统中的线程状态转换
首先我们来看看操作系统中的线程状态转换。
在现在的操作系统中,线程是被视为轻量级进程的,所以操作系统线程的状态其实和操作系统进程的状态是一致的。
4.2.1 NEW
处于NEW状态的线程此时尚未启动。这里的尚未启动指的是还没调用Thread实例的start()方法。
/** * Thread state for a thread which has not yet started. */关于start()的两个引申问题
/* Java thread status for tools,
* initialized to indicate thread 'not yet started'
*/
private volatile int threadStatus = 0;public synchronized void start() {
/**
* This method is not invoked for the main method thread or "system"
* group threads created/set up by the VM. Any new functionality added
* to this method in the future may have to also be added to the VM.
*
* A zero status value corresponds to state "NEW".
*/
if (threadStatus != 0)
throw new IllegalThreadStateException();
//...
}实验下执行2次start,会发现第二次threadStatus不为0了,抛出异常。
4.2.2 RUNNABLE
表示当前线程正在运行中。处于RUNNABLE状态的线程在Java虚拟机中运行,也有可能在等待CPU分配资源。
4.2.3 BLOCKED
阻塞状态。处于BLOCKED状态的线程正等待锁的释放以进入同步区。
我们用BLOCKED状态举个生活中的例子:
假如今天你下班后准备去食堂吃饭。你来到食堂仅有的一个窗口,发现前面已经有个人在窗口前了,此时你必须得等前面的人从窗口离开才行。
假设你是线程t2,你前面的那个人是线程t1。此时t1占有了锁(食堂唯一的窗口),t2正在等待锁的释放,所以此时t2就处于BLOCKED状态4.2.4 WAITING
等待状态。处于等待状态的线程变成RUNNABLE状态需要其他线程唤醒。
调用如下3个方法会使线程进入等待状态:
- Object.wait():使当前线程处于等待状态直到另一个线程唤醒它;
- Thread.join():等待线程执行完毕,底层调用的是Object实例的wait方法;
- LockSupport.park():除非获得调用许可,否则禁用当前线程进行线程调度。
4.2.5 TIMED_WAITING
超时等待状态。线程等待一个具体的时间,时间到后会被自动唤醒。
调用如下方法会使线程进入超时等待状态:
- Thread.sleep(long millis):使当前线程睡眠指定时间;
- Object.wait(long timeout):线程休眠指定时间,等待期间可以通过notify()/notifyAll()唤醒;
- Thread.join(long millis):等待当前线程最多执行millis毫秒,如果millis为0,则会一直执行;
- LockSupport.parkNanos(long nanos): 除非获得调用许可,否则禁用当前线程进行线程调度指定时间;
- LockSupport.parkUntil(long deadline):同上,也是禁止线程进行调度指定时间
4.2.6 TERMINATED
终止状态。此时线程已执行完毕。
终止状态。此时线程已执行完毕。
4.3 线程状态的转换
根据上面关于线程状态的介绍我们可以得到下面的线程状态转换图:
4.4 常用方法
Thread类的常用方法如下:
void start();//启动线程 void sleep(long);//让线程进入睡眠,让出cpu资源,不释放锁 void yield();//让步,让出CPU资源,给予同级别和更高优先级的线程获取资源的机会,也可能自己再次获取资源 void interrupt();//设置中断标识为true void interrupted();//第一次调用将中断标识设置为true,第二次设置为false void isInterrupted();//判断中断标识是否为true void join();//主线程等待调用此方法的线程执行结束 void join(long);//主线程等待调用此方法的线程执行结束/超时Obecjt类(涉及到锁)的常用方法如下:
void wait();//让线程进入等待,让出CPU资源,释放锁 void wait(long); void notify();//唤醒一个(随机)在此对象监视器上等待的线程 void notifyAll();//唤醒所有在此对象监视器上等待的线程一个sleep/wait例子
public class TreadDemo { private static Object lock = new Object(); public static void main(String[] args) throws InterruptedException { new Thread(new HelloA()).start(); Thread.currentThread().sleep(100);//主线程沉睡100毫秒,保证A先执行 new Thread(new HelloB()).start();//B需要获取锁,才能执行 new Thread(new HelloC()).start();//C无效获取锁就能执行 } public static class HelloA implements Runnable{ @Override public void run() { synchronized(lock){ System.out.println("A进来了"); try { Thread.currentThread().sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("hello A"); } } } public static class HelloB implements Runnable{ @Override public void run() { synchronized(lock){ System.out.println("B进来了"); } } } public static class HelloC implements Runnable{ @Override public void run() { System.out.println("C进来了"); } } }结果:可以看出线程A调用了sleep方法让出了CPU资源,线程C执行了,但是线程B没获得锁,需要等待线程A释放锁,所以线程A继续执行,线程B等A执行完后才执行。
A进来了 C进来了 hello A B进来了 Process finished with exit code 0我们改下方法,调用Object.wait方法
public static class HelloA implements Runnable{ @Override public void run() { synchronized(lock){ System.out.println("A进来了"); try { lock.wait(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("hello A"); } } }结果:可以看出B获取到锁了,执行了。
A进来了 B进来了 C进来了 hello A线程中断
在某些情况下,我们在线程启动后发现并不需要它继续执行下去时,需要中断线程。目前在Java里还没有安全直接的方法来停止线程,但是Java提供了线程中断机制来处理需要中断线程的情况。
线程中断机制是一种协作机制。需要注意,通过中断操作并不能直接终止一个线程,而是通知需要被中断的线程自行处理。
简单介绍下Thread类里提供的关于线程中断的几个方法:
void interrupt();//设置中断标识为true void interrupted();//第一次调用将中断标识设置为true,第二次设置为false void isInterrupted();//判断中断标识是否为true
public static void main(String[] args){ new Thread(new ThreadA()).start(); } public static class ThreadA implements Runnable{ @Override public void run() { while (true){ System.out.println(Thread.currentThread().isInterrupted()); System.out.println("运行中..."); Thread.currentThread().interrupt();//只是设置标志位而已,不能停止线程 } } } }
public static class ThreadA implements Runnable{ @Override public void run() { int i = 0; while (true){ System.out.println(Thread.currentThread().isInterrupted()); System.out.println("运行中..."); Thread.currentThread().interrupt(); try { if(i == 10){ Thread.currentThread().interrupt();//设置退出标识 } Thread.currentThread().sleep(1000);//sleep根据中断标识位为true抛出异常 i++; } catch (InterruptedException e) { break;//捕获interruptException,退出循环 } } System.out.println("线程结束");//线程退出了 } }yield方法例子,结果:线程A的其他线程未执行:“我进来了”,证明线程A的锁没释放。线程B打印了,说明线程A的CPU资源有释放。
public class ThreadYieldDemo { private static Object lock = new Object(); public static void main(String[] args){ for(int i = 0; i < 10; i++){ new Thread(new ThreadA()).start(); } new Thread(new ThreadB()).start(); } public static class ThreadA implements Runnable{ @Override public void run() { synchronized(lock){ System.out.println("我进来了"); Thread.currentThread().yield(); System.out.println("我没放弃锁啊"); while (true){ } } } } public static class ThreadB implements Runnable{ @Override public void run() { try { Thread.currentThread().sleep(10000); } catch (InterruptedException e) { e.printStackTrace(); } int i = 10; do { System.out.println("我是另一个哈"); i--; }while (i == 0); } } }
4.5 守护线程与用户线程
守护线程:中后台提供通用性支持的线程,比如垃圾回收线程。
用户线程:用户的业务线程。
区别:守护线程是最低优先级的,它不持有任何的系统资源,当所有的非守护线程结束的时候,程序也就终止了。
用户线程中start前,可以调用setDaemon方法来转换为守护线程。守护线程创建的子线程也是守护线程。
原文地址:http://redspider.group:4000/article/01/4.html
感恩作者


浙公网安备 33010602011771号