线程的生命周期
-
在线程的生命周期中,它要经过新建(new)、就绪(Runnable)、运行(Running)、阻塞(Blocked)、和死亡(Dead)5种状态。
- 当程序使用new()关键字创建了一个线程后,该线程就处于新建状态。
- 当调用start()方法,线程就处于就绪状态,处于此状态的线程可以运行,但它何时开始调度运行,取决于JVM里的线程调度器,即取决于CPU,人为不能干涉。
- 只能对处于新建状态的线程调用Start()方法,否则将会引发IllegalThreadStatteException
- 处于就绪状态的线程获得了CPU,开始执行run()方法的线程执行体,则该线程处于运行状态,它不可能一直处于运行状态,除非它的执行体非常短,线程在运行过程中需要被中断,以便其它线程获得运行的机会。
- 当线程调用sleep()方法、阻塞式IO方法或者等某个通知等时,线程会进入阻塞状态,阻塞状态解除后,线程重新进入就绪状态,等待调用。
- 线程结束后处于死亡状态:
- run()方法或call()方法执行完成,线程正常结束。
- 线程抛出一个未捕获的Exception或Error
- 调用该线程的stop()方法来结束该线程–容易导致死锁,不推荐使用。
线程中的一些方法
-
join方法
可以让一个线程等待另一个线程完成的方法。
public class testJoin implements Runnable { public static void main(String[] args) throws InterruptedException { Thread thread = new Thread(new testJoin()); thread.start(); for (int i = 0; i < 100; i++) { if(i==50){ //i=50时,thread线程的join()方法被调用,main()线程阻塞,直到thread执行结束,main线程才会继续执行 thread.join(); } System.out.println("main "+i); } } @Override public void run() { for (int i = 0; i < 1000; i++) { System.out.println("join "+i); } } } //由于线程调度取决于cpu,所以每次运行结果可能不一样,但是每次运行到 main 49时,接下来下来出现的一定是join+i,直到join 999,main 50才出现并继续往下走。部分结果截图如下:
-
线程睡眠 sleep
该方法可以让当前正在执行的线程休眠一段时间,进入阻塞状态。
//简单的倒计时器 public class Times { public static void main(String[] args) throws InterruptedException { long time; Scanner scanner = new Scanner(System.in); System.out.println("请输入倒计时的秒数:"); time = scanner.nextLong(); System.out.println("计时开始:"); timer(time); } private static void timer(long time) throws InterruptedException { System.out.println("倒计时:"+time); for (long i = time-1; i>0; i--) { //调用sleep()方法,i每加一次,暂停1秒 Thread.sleep(1000); System.out.println("倒计时:"+i); } System.out.println("时间到!"); } }
-
线程让步 yield
该方法和sleep()方法有点相似,它也可以使正在运行的线程暂停,不过它是将该线程转为就绪状态,不会阻塞该线程。所以可能会出现:当某个线程调用了yield()方法,并没有成功让步。因为该方法被转为就绪状态后,又立刻被cpu重新调度执行了。
public class DemoYield { public static void main(String[] args) { MyYield yield = new MyYield(); new Thread(yield,"1").start(); new Thread(yield,"2").start(); } } class MyYield implements Runnable{ @Override public void run() { System.out.println(Thread.currentThread().getName()+"--启动"); Thread.yield();//礼让---(不一定成功) System.out.println(Thread.currentThread().getName()+"--停止"); } } //礼让是否成功:取决于cpu //执行结果1-礼让成功 1--启动 2--启动 1--停止 2--停止 //执行结果2-礼让不成功 1--启动 1--停止 2--启动 2--停止
-
改变线程优先级
- 每个线程都具有自己的优先级。
- 线程调度器会按照线程优先级来决定应该调度哪个线程执行。
- 优先级高的线程只是得到的执行机会比优先级低的得到的执行机会多,也就是获得调度的概率更高一些。
- Thread提供了**setPriority()和getPriority()**方法来设置或获得指定线程的优先级。
- setPriority()的参数可以是一个整数(1-10),也可以是Thread类的如下三个静态常量:
- MIN_PRIORITY——值为1
- MAX_PRIORITY——值为10
- NORN_PRIORITY——值为5
public class DemoPriority {
public static void main(String[] args) {
System.out.println(Thread.currentThread().getName()+"-->其优先级为:"+Thread.currentThread().getPriority());
MyPriority myPriority = new MyPriority();
Thread thread1 = new Thread(myPriority,"a线程");
Thread thread2 = new Thread(myPriority,"b线程");
Thread thread3 = new Thread(myPriority,"c线程");
Thread thread4 = new Thread(myPriority,"d线程");
Thread thread5 = new Thread(myPriority,"e线程");
thread1.setPriority(1);
thread1.start();
thread2.setPriority(2);
thread2.start();
thread3.setPriority(10);
thread3.start();
thread4.setPriority(6);
thread4.start();
thread5.setPriority(5);
thread5.start();
}
}
class MyPriority implements Runnable{
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"-->其优先级为:"+Thread.currentThread().getPriority());
}
}
//运行结果(再强调一次:因为线程调度取决于cpu,因而每次的运行结果不一定相同)
main-->其优先级为:5
c线程-->其优先级为:10
d线程-->其优先级为:6
e线程-->其优先级为:5
a线程-->其优先级为:1
b线程-->其优先级为:2
-
守护线程(也叫后台线程或精灵线程)
- 有一种线程,它是在后台运行的他的任务是为其他的线程提供服务,这种线程被称为守护线程。
- JVM的垃圾回收线程就是典型的后台线程。
- 调用Thread对象的setDaemon(true)方法,可以将指定线程设置为后台线程。该方法必须在start()方法之前调用,否则会引发IllegalThreadStateException异常。
- 如果所有的前台线程(用户线程)都死亡,后台线程会自动死亡。
- 虚拟机必须确保所有的前台线程执行完毕,但不用等待后台线程执行完毕。所以当所有前台线程都死亡时(也就是说整个虚拟机只剩下后台线程时),程序就没有执行的必要了,虚拟机就会退出(不会管后台线程是否执行完毕)。
public class TestDaemon extends Thread { public void run(){ for (int i = 0; i < 1000; i++) { System.out.println(getName()+"--"+i); } } public static void main(String[] args) { TestDaemon testDaemon = new TestDaemon(); //将此线程设置为后台线程,必须在启动它之前设置 testDaemon.setDaemon(true); testDaemon.start(); for (int i = 0; i < 10; i++) { System.out.println(Thread.currentThread().getName()+"--"+i); } //程序执行到此处,前台线程(main)执行结束,JVM会主动退出,后台线程无法运行到999就会结束 //后台线程之所以在main--9后还会执行,是因为在前台线程死亡后,JVM会通知后台线程死亡,但从它接收指令到做出相应,需要一定时间。 } } //运行结果 main--0 Thread-0--0 main--1 Thread-0--1 main--2 Thread-0--2 main--3 Thread-0--3 main--4 Thread-0--4 main--5 Thread-0--5 main--6 Thread-0--6 main--7 Thread-0--7 main--8 Thread-0--8 main--9 Thread-0--9 Thread-0--10 Thread-0--11 Thread-0--12 Thread-0--13 Thread-0--14 Thread-0--15 Thread-0--16 Thread-0--17 Thread-0--18 Thread-0--19 Thread-0--20 Thread-0--21 Thread-0--22 Thread-0--23 Thread-0--24 Thread-0--25 Thread-0--26 Thread-0--27 Process finished with exit code 0