线程学习二:Thread类常用方法
在正式学习Thread类中的具体方法之前,我们先来了解一下线程有哪些状态,这个将会有助于后面对Thread类中的方法的理解。
线程从创建到最终的消亡,要经历若干个状态。一般来说,线程包括以下这几个状态:创建(new)、就绪(runnable)、运行(running)、阻塞(blocked)、time waiting、waiting、消亡(dead)。
当需要新起一个线程来执行某个子任务时,就创建了一个线程。但是线程创建之后,不会立即进入就绪状态,因为线程的运行需要一些条件(比如内存资源,在前面的JVM内存区域划分一篇博文中知道程序计数器、Java栈、本地方法栈都是线程私有的,所以需要为线程分配一定的内存空间),只有线程运行需要的所有条件满足了,才进入就绪状态。
当线程进入就绪状态后,不代表立刻就能获取CPU执行时间,也许此时CPU正在执行其他的事情,因此它要等待。当得到CPU执行时间之后,线程便真正进入运行状态。
线程在运行状态过程中,可能有多个原因导致当前线程不继续运行下去,比如用户主动让线程睡眠(睡眠一定的时间之后再重新执行)、用户主动让线程等待,或者被同步块给阻塞,此时就对应着多个状态:time waiting(睡眠或等待一定的事件)、waiting(等待被唤醒)、blocked(阻塞)。
当由于突然中断或者子任务执行完毕,线程就会被消亡。
下面这副图描述了线程从创建到消亡之间的状态:
在有些教程上将blocked、waiting、time waiting统称为阻塞状态,这个也是可以的,只不过这里我想将线程的状态和Java中的方法调用联系起来,所以将waiting和time waiting两个状态分离出来。
常用方法
1、start
start()用来启动一个线程,当调用start方法后,系统才会开启一个新的线程来执行用户定义的子任务,在这个过程中,会为相应的线程分配需要的资源。
2、run
run()方法是不需要用户来调用的,当通过start方法启动一个线程之后,当线程获得了CPU执行时间,便进入run方法体去执行具体的任务。注意,继承Thread类必须重写run方法,在run方法中定义具体要执行的任务。
3、yield
yield()方法详单与线程调度器,执行该方法后当前线程将交出其CPU使用权限,让别的线程有机会使用CPU。
实际上,yield()方法对应了如下操作;先检测当前是否有相同优先级的线程处于同可运行状态,如有,则把CPU的占有权交给次线程,否则继续运行原来的线程,所以yield()方法称为“退让”,它把运行机会让给了同等级的其他线程。
1 public class MainThreadTest implements Runnable { 2 3 public static void main(String[] args) { 4 Thread aa = new Thread(new MainThreadTest()); 5 Thread bb = new Thread(new MainThreadTest()); 6 aa.start(); 7 bb.start(); 8 } 9 10 @Override 11 public void run() { 12 for (int i = 0; i < 100; i++) { 13 System.out.println(Thread.currentThread().getName() + i); 14 Thread.yield(); 15 } 16 } 17 }
4、sleep
调用sleep(long millis)将任务终止mills毫秒
1 public class ThreadTest implements Runnable { 2 private int count = 300; 3 4 @Override 5 public void run() { 6 for (int i = 0; i < 100; i++) { 7 System.out.println(Thread.currentThread().getName() + "........" + count--); 8 try { 9 Thread.sleep(1000); 10 } catch (InterruptedException e) { 11 // TODO Auto-generated catch block 12 e.printStackTrace(); 13 } 14 } 15 } 16 17 public static void main(String[] args) { 18 ThreadTest aa = new ThreadTest(); 19 Thread t1 = new Thread(aa); 20 Thread t2 = new Thread(aa); 21 Thread t3 = new Thread(aa); 22 t1.start(); 23 t2.start(); 24 t3.start(); 25 26 } 27 28 }
带同步锁调用sleep()方法时
1 public class MainThreadTest implements Runnable { 2 private Object object = new Object(); 3 4 public static void main(String[] args) throws InterruptedException { 5 MainThreadTest mtt = new MainThreadTest(); 6 Thread aa = new Thread(mtt); 7 Thread bb = new Thread(mtt); 8 aa.start(); 9 bb.start(); 10 } 11 12 @Override 13 public void run() { 14 synchronized (object) { 15 System.out.println(Thread.currentThread().getName() + "进入随眠状态"); 16 try { 17 Thread.sleep(1000); 18 } catch (InterruptedException e) { 19 e.printStackTrace(); 20 } 21 System.out.println(Thread.currentThread().getName() + "结束随眠状态"); 22 } 23 } 24 }
5、getPriority和setPriority
用来查看和设置线程的优先级,优先级为1到10,默认为5,优先级越大占有CPU时间越久(相对于优先级第的而言,并不是绝对的)
1 public class ThreadExtendsTest extends Thread { 2 public int priority; 3 4 public ThreadExtendsTest(int priority) { 5 super(); 6 this.priority = priority; 7 } 8 9 @Override 10 public void run() { 11 Thread.currentThread().setPriority(priority); 12 System.out.println(Thread.currentThread().getName() + Thread.currentThread().getPriority()); 13 for (int i = 0; i < 100; i++) { 14 System.out.println(this.getName() + "..." + i); 15 16 } 17 } 18 19 public static void main(String[] args) { 20 ThreadExtendsTest aa = new ThreadExtendsTest(Thread.MIN_PRIORITY); 21 ThreadExtendsTest bb = new ThreadExtendsTest(Thread.MAX_PRIORITY); 22 aa.start(); 23 bb.start(); 24 } 25 26 }
注意:getPriority和setPriority都有final修饰,是关于线程的属性,调用这两个方法时需要先调用currentThread()方法,类似的还有getId、getName、setName等方法。
6、jion
Join()方法把指定的线程加入到当前线程,可以将两个交替执行的线程合并为顺序执行的线程。比如在线程B中调用了线程A的Join()方法,直到线程A执行完毕后,才会继续执行线程B。
1 public class MainThreadTest implements Runnable { 2 3 public static void main(String[] args) throws InterruptedException{ 4 Thread aa = new Thread(new MainThreadTest()); 5 aa.start(); 6 for (int i = 0; i < 100; i++) { 7 System.out.println(Thread.currentThread().getName() + i); 8 aa.join(); 9 } 10 } 11 12 @Override 13 public void run() { 14 for (int i = 0; i < 100; i++) { 15 System.out.println(Thread.currentThread().getName() + i); 16 } 17 } 18 }
7.interrupt
Thread.interrupt()方法不会中断一个正在运行的线程。它的作用是,在线程受到阻塞时抛出一个中断信号,这样线程就得以退出阻塞的状态。更确切的说,如果线程被Object.wait, Thread.join和Thread.sleep三种方法之一阻塞,那么,它将接收到一个中断异常(InterruptedException),从而提早地终结被阻塞状态。
在上面已经说到了Thread类中的大部分方法,那么Thread类中的方法调用到底会引起线程状态发生怎样的变化呢?如图所示:
参考资料:
《Java编程思想》
http://www.cnblogs.com/dolphin0520/
http://zy19982004.iteye.com/blog/1626916
http://www.cnblogs.com/DreamSea/archive/2012/01/11/JavaThread.html#navigation
http://www.blogjava.net/vincent/archive/2008/08/23/223912.html
http://iteye.blog.163.com/blog/static/1863080962012111424544215/
http://blog.csdn.net/lifei128/article/details/20363257