上篇随笔介绍了线程的相关基础知识以及新启线程的几种方法,本片将继续介绍线程的生命周期及方法详解。
一、线程的生命周期
在Thread代码中,线程的状态被分为6种
public enum State { /** * 尚未启动的线程处于此状态 */ NEW, /** * 在java虚拟机中执行或等待其他资源的线程处于此状态 */ RUNNABLE, /** * 被阻塞等待监视器锁定的线程处于此状态*/ BLOCKED, /** * 正在等待另一个线程执行特定动作的线程处于此状态*/ WAITING, /** * 正在等待另一个线程执行动作到达指定等待时间的线程处于此状态*/ TIMED_WAITING, /** * 已退出的线程处于此状态 */ TERMINATED; }
而通常我们讨论的状态为5种:
- 新建(New):创建尚未启动的线程;
- 就绪(Runnable):此时线程已经具备运行的能力,是“可执行状态”,但尚未运行,即调用了Thread类的start();
- 运行(Running):线程获得了CPU权限,由就绪状态进入运行状态(线程也只能由就绪状态进入运行状态);
- 阻塞(Blocked):阻塞状态是因为线程因为某种原因放弃CPU执行权,暂时停止运行。直到线程进入就绪状态,才有机会进入就绪状态,阻塞的情况分为3种:(1)等待阻塞--通过调用wait()方法,使线程等待某工作完成;(2)同步阻塞--线程获取synchronized同步锁失败,锁被其他线程占用;(3)其他阻塞--通过调用线程的sleep()或者join()发出IO请求时,线程会进入阻塞状态,当sleep状态超时或join等待线程终止、超时、或者IO处理完成是,线程重新进入就绪状态;
- 死亡(Dead):线程执行完了或者因为一场退出了run()方法,线程结束生命周期。
二、Thread常用方法
1、常用构造方法:
Thread();
Thread(Runnable target) ;
Thread(Runnable target, String name);
Thread(String name)
2、设置线程属性的方法:
public final void setDaemon(boolean on)//将此线程标记为daemon线程或用户线程 public final synchronized void setName(String name)//将此线程的名称更改为等于参数name 。 public void setContextClassLoader(ClassLoader cl)//设置此线程的上下文ClassLoader public final void setPriority(int newPriority)//更改此线程的优先级
3、获取线程属性的方法
public long getId()//返回此线程的标识符 public final String getName()//返回此线程的名称 public ClassLoader getContextClassLoader()//返回此Thread的上下文ClassLoader public final int getPriority()//返回此线程的优先级 public Thread.State getState()//返回此线程的状态 public final ThreadGroup getThreadGroup()//返回此线程所属的线程组
4、start()方法
public synchronized void start()
start()方法启动线程,开始调用线程的run()方法。
5、run()方法
public void run()
run()是线程在执行状态下需要执行的方法,线程调用start()之后会调用该方法。不能直接使用该方法,直接使用的话,并不是通过线程调用,而是仅仅作为一个普通对象的普通方法调用。请看如下例子:
class MyThread extends Thread{ public MyThread(String name){ super(name); } @Override public void run() { System.out.println("当前是[" + Thread.currentThread().getName() + "]线程!"); } } public class Test{ public static void main(String[] args) throws InterruptedException { Thread thread1 = new MyThread("线程1"); Thread thread2 = new MyThread("线程2"); thread1.start(); Thread.sleep(500); thread2.run(); } }
结果如下:
可以看到直接调用thread2.run(),新建的线程对象并没有启动,当前线程仍然是main线程。
7、currentThread()
public static native Thread currentThread;//返回对当前正在执行的线程对象的引用
currentThread()方法是静态本地方法,不是通过线程对象调用,而是通过Thread类调用。
6、sleep()方法
public static native void sleep(long millis) throws InterruptedException;
7、sleep()方法也是静态本地方法,通过thread调用。
class MyThread extends Thread{ public MyThread(String name){ super(name); } @Override public void run() { System.out.println("当前是[" + Thread.currentThread().getName() + "]线程!"); long time1 = System.currentTimeMillis(); try { Thread.sleep(500); long time2 = System.currentTimeMillis(); System.out.println("[" + Thread.currentThread().getName() + "]线程休眠" + (time2-time1) + "毫秒!"); } catch (InterruptedException e) { e.printStackTrace(); } } } public class Test{ public static void main(String[] args) throws InterruptedException { for (int i=0;i<3;i++){ Thread thread = new MyThread("线程" +i); thread.start(); } } }
执行结果:
8、isAlive()方法
public final native boolean isAlive();//测试这个线程是否活着,线程已经被启动,并且尚未死亡
9、interrupt()、isInterrupted()和interrupted()
public void interrupt() //中断这个线程(线程实例) /** * 测试当前线程是否中断。 该方法可以清除线程的中断状态 。 * 换句话说,如果这个方法被连续调用两次,那么第二个调用将返回false * (除非当前线程再次中断,在第一个调用已经清除其中断状态之后,在第二个调用之前已经 * 检查过)。 * 忽略线程中断,因为线程在中断时不存在将被该方法返回false所反映 */ public static boolean interrupted() { return currentThread().isInterrupted(true); } /** * 测试这个线程是否被中断。 线程的中断状态不受此方法的影响。(线程实例) */ private native boolean isInterrupted(boolean ClearInterrupted)
interrupt()方法用来中断请求,调用它时,并不是直接将线程中断,而是将线程的中断状态位改为中断状态,isInterrupted()返回为true;interrupt()的作用更像是通知线程中断,线程选择执行完任务后再中断。
下面两个例子更好的解释这种原理:
-
class MyThread extends Thread{ @Override public void run() { for (int i=0;i<1000;i++){ System.out.println("是否已下达中断指令?" + isInterrupted()); } } } public class Test{ public static void main(String[] args) throws InterruptedException { Thread thread = new MyThread(); thread.start(); Thread.sleep(10); thread.interrupt(); } }
执行结果:
可以看到,在调用interrupt()方法后,线程的run()方法仍在执行,直到正常执行结束。
-
class MyThread extends Thread{ @Override public void run() { for (int i=0;i<1000;i++){ System.out.println("是否已下达中断指令?" + interrupted()); } } } public class Test{ public static void main(String[] args) throws InterruptedException { Thread thread = new MyThread(); thread.start(); Thread.sleep(10); thread.interrupt(); } }
执行结果:
可以看到,再调用interrupt()方法后,中断标志位已经修改为true,interrupted()的第一次调用返回true,后面调用均返回false。这是因为,当中断标志位被interrupt()修改位true时,在调用interrupted(),确实会返回true,但这时本地方法interrupted()会将中断标志位恢复,所以后面调用均显示false。
同时需要注意interrupted()返回的是当前线程的标志位!!!!例子:
class MyThread extends Thread{ @Override public void run() { for (int i = 0; i < 10; i++) { System.out.println("i="+(i+1)); } } } public class Test{ public static void main(String[] args) throws InterruptedException { MyThread thread=new MyThread(); thread.start(); thread.interrupt(); System.out.println("第一次调用thread.isInterrupted():"+thread.isInterrupted()); System.out.println("第二次调用thread.isInterrupted():"+thread.isInterrupted()); //测试interrupted()函数 System.out.println("第一次调用thread.interrupted():"+thread.interrupted()); System.out.println("第二次调用thread.interrupted():"+thread.interrupted()); System.out.println("thread是否存活:"+thread.isAlive()); } }
上一个例子中,两次interrupted()方法都返回false,是因为该方法在main方法中调用,因此反应的并不是MyThread实例对象是否中断,而是main线程是否中断!!!!
interrupt()并不是真正意义上的中断线程,而是通知线程该中断了。
对于一个线程,调用interrupt()时:
①正常执行任务时,当线程处于阻塞状态(例如处于sleep, wait, join 等状态),线程会立即退出阻塞状态,并抛出一个InterruptedException异常。仅此而已。
②当线程处于正常执行状态,则将该线程的中断标志改为true,并不影响线程的正常运行。
stop()虽然可以是线程终止,但是并不能一定及时释放资源,已废弃。
当确实需要即刻中断线程时,可以这样做:
①在正常运行任务时,经常检查本线程的中断标志位,如果被设置了中断标志就自行停止线程。(通过while关键字)
②在调用阻塞方法时正确处理InterruptedException异常。(例如,catch异常后就结束线程,return关键字结束线程。)
10、yield()方法
public static native void yield();//暂停当前正在执行的线程对象,并执行其他线程使用较少
①yield()使当前线程交出CPU权限,是CPU去执行其他线程;
②yield()方法和sleep()方法类似,不会释放锁,但yield()方法不能控制具体交出CPU的时间;
③yield()只能使相同优先级的线程获取CPU执行的机会;
④yield()不会是线程进入阻塞状态,只会使线程重新进入就绪状态,等待获取CPU执行权的机会。
11、join方法
public final void join() throws InterruptedException { join(0); } public final synchronized void join(long millis) throws InterruptedException; public final synchronized void join(long millis, int nanos) throws InterruptedException;
join()方法是等待调用方法的线程死亡,意思是该线程需要先执行完毕才会执行后面的线程。
例子:
class MyThread extends Thread{ public MyThread(String name){ setName(name); } @Override public void run() { System.out.println("[" + Thread.currentThread().getName() + "]执行..."); } } /** * */ public class Test{ public static void main(String[] args) throws InterruptedException { Thread thread1 = new MyThread("线程1"); thread1.start(); thread1.join(); System.out.println("没有join()会优先打印这条语句!!!"); } }
执行结果:
12、stop()、resume()、suspend()均已不建议使用,就不介绍了。