1、线程的生命周期
线程的生命周期包含6个阶段,包括:新建(初始)、运行(就绪+运行中)、阻塞、等待、超时等待、销毁。
-
初始(NEW):新创建了一个线程对象,但还没有调用start()方法。
-
运行(RUNNABLE):Java线程中将就绪(ready)和运行中(running)两种状态笼统的称为“运行”。
线程对象创建后,其他线程(比如main线程)调用了该对象的start()方法。该状态的线程位于可运行线程池中,等待被线程调度选中,获取CPU的使用权,此时处于就绪状态(ready)。就绪状态的线程在获得CPU时间片后变为运行中状态(running)。 -
阻塞(BLOCKED):表示线程阻塞于锁。
-
等待(WAITING):进入该状态的线程需要等待其他线程做出一些特定动作(通知或中断)。
-
超时等待(TIMED_WAITING):该状态不同于WAITING,它可以在指定的时间后自行返回。
-
终止(TERMINATED):表示该线程已经执行完毕。
线程生命周期图解:
1.1、初始状态
实现Runnable接口和继承Thread可以得到一个线程类,new一个实例出来,线程就进入了初始状态。
1.2、就绪状态
- 就绪状态只是说你资格运行,调度程序没有挑选到你,你就永远是就绪状态。
- 调用线程的start()方法,此线程进入就绪状态。
- 当前线程sleep()方法结束,其他线程join()结束,等待用户输入完毕,某个线程拿到对象锁,这些线程也将进入就绪状态。
- 当前线程时间片用完了,调用当前线程的yield()方法,当前线程进入就绪状态。
- 锁池里的线程拿到对象锁后,进入就绪状态。
1.3、运行中状态
线程调度程序从可运行池中选择一个线程作为当前线程时线程所处的状态。这也是线程进入运行状态的唯一一种方式。
1.4 、阻塞状态
阻塞状态是线程阻塞在进入synchronized关键字修饰的方法或代码块(获取锁)时的状态。
1.5、 等待
处于这种状态的线程不会被分配CPU执行时间,它们要等待被显式地唤醒,否则会处于无限期等待的状态。
1.6、 超时等待
处于这种状态的线程不会被分配CPU执行时间,不过无须无限期等待被其他线程显示地唤醒,在达到一定时间后它们会自动唤醒。
1.7、 终止状态
- 当线程的run()方法完成时,或者主线程的main()方法完成时,我们就认为它终止了。这个线程对象也许是活的,但是,它已经不是一个单独执行的线程。线程一旦终止了,就不能复生。
- 在一个终止的线程上调用start()方法,会抛出java.lang.IllegalThreadStateException异常。
2、线程的创建
-
1)继承Thread类
-
2) 实现Runable
-
3)实现Callable
/** * step1:implements Callable * work out:Callable有类型参数泛型,如下面的<String>;该类型泛型确定了call()的返回结果类型。不指定就是Object */ public class CreateThreadByImpCallable implements Callable<String>{ private String param; public CreateThreadByImpCallable(String param){ this.param = param; } /** * step2:implements call method */ @Override public String call() throws Exception { System.out.println(this.param+"call method operate return"); return this.param+"call method operate return"; } } CreateThreadByImpCallable createThreadByImpCallable = new CreateThreadByImpCallable("createThread by implements Callable"); /** * step 3:as parameter of FutureTask Constructed function * Callable depends FutureTask */ FutureTask<String> task = new FutureTask<String>(createThreadByImpCallable); Thread thread3 = new Thread(task); thread3.start();
4)ThreadPool
-
线程创建方法比较:
1、实现Runnable、Callable接口较继承Thread的优点
1)、避免JAVA单继承限制
2)、代码可以被多个线程共享
3)、适合多个相同程序代码的线程去处理同一资源的情况
4)、线程池中只能放入实现了Runable或Callable的线程,不能放入继承Thread的线程。2、实现Runnable和实现Callable的区别
1)、实现Callable的线程可以返回执行结果;而实现Runnable的线程没法返回执行结果。
2)、实现Callable需要实现call()且允许抛出异常;而实现Runnable的线程需要实现run()但不允许抛出异常,只能内部处理。
3)、加入线程池,Runnable使用ExecutorService的execute方法,Callable使用submit方法。0+注意:
1)、Callable支持返回执行结果,但是调用的是FutureTask的get方法,同时该方法会阻塞主线程直到获取返回结果,不调用该方法,主线程不会阻塞。
3、线程相关方法介绍
-
sleep
public static void sleep(long millis, int nanos) //休眠mills+1毫秒 public static native void sleep(long millis) throws InterruptedException;
PS: 1)Thread类静态方法
2)表示当前线程休眠指定时间,当前线程即表示调用该方法的线程
3)不释放对象锁
-
yield
public static native void yield();
调用此方法会提醒调度器我愿意放弃当前的cpu资源,如果CPU资源不紧张的话,调度器可能会忽略这个提醒。操作系统是为每个线程分配一个时间片来占有CPU的,正常情况下当一个线程把分配给自己的时间片使用完后,线程调度器才会进行下一轮的线程调度,而当一个线程调用了Thread类的静态方法yield时,是在告诉线程调度器自己占有的时间片中还没有使用完的部分自己不想使用了,这暗示线程调度器现在就可以进行下一轮的线程调度。
**PS:**1)Thread类静态方法
2)不释放对象锁
3)只能让同优先级的线程有执行的机会
-
yield和Sleep的区别
sleep方法调用时线程会被休眠指定的时间,进入超时等待状态,在这期间线程调度器不会去调度该线程。
yield 方法调用时,线程只是让出自己剩余的时间片,进入就绪状态(Runnable),线程调度器下一次调度时就又可能调度到当前线程执行 。
sleep 方法允许较低优先级的线程获得运行机会,但yield()方法执时,当前线程仍处在可运行状态,所以不可能让出较低优先级的线程此时获取CPU占有权。
在一个运行系统中,如果较高优先级的线程没有调用sleep方法,也没有受到I/O阻塞,那么一般较低优先级线程只能等待所有较高优先级的线程运行结束,方可有机会运行。
-
interrupt
public void interrupt();
Thread类实例方法,在线程内部存在着名为interrupt flag的标识,如果一个线程调用了interrupt方法,flag会被设置,但是如果当前线程正处于阻塞状态时,调用interrupt,线程将会中断阻塞,并且会抛出InterruptedException异常,这个异常就像是一个signal(信号)一样通知当前线程被打断了,并且flag会被清除。
-
isInterrupted
public boolean isInterrupted();
Thread类实例方法,判断线程是否被中断。
-
interrupted
public static boolean interrupted();
Thread类静态方法,判断当前线程是否被中断。
-
interrupted和isInterrupted方法的区别:
1)interrupted()是静态方法,但是isInterrupted()是实例方法;
2)interrupted()会清空线程中断状态,isInterrupted()不会清空线程中断状态。
-
join
join() 一直等待 join(long millis) 等待指定毫秒数 join(long millis, int nanos) 等待指定毫秒数
Thread类实例方法,等待这个线程结束;也就是说,t.join()方法阻塞调用此方法的线程(calling thread)进入 TIMED_WAITING 状态,直到线程t完成,此线程再继续。
Join方法实现是通过wait()。 当main线程调用t.join时候,main线程会获得线程对象t的锁(wait 意味着拿该线程t对象作为锁),调用该线程对象t的wait(),直到该线程对象t唤醒main线程 ,比如退出后。这就意味着main 线程调用t.join时,必须能够拿到该线程对象t的锁。
-
wait、notify、notifyAll
1)wait方法:使当前线程暂停执行并释放对象锁标示,让其他线程可以进入synchronized数据块,当前线程被放入锁对象等待池中。
2)notify()方法:将从锁对象等待池中移走一个任意的线程并放到锁标志等待池中,只有锁对象等待池中线程能够获取锁标志;如果对象等待池中没有线程,则notify()不起作用。
3)notifyAll()方法:将锁对象等待池中所有线程移动到锁标志等待池中。
PS:
1)wait、notify、notifyAll三个方法都是java.lang.Object方法
2)wait、notify、notifyAll三个方法用于协调多个线程对共享数据的存取,所以必须在synchronized语句块中使用。
3)wait和notify/notifyAll锁操作的必须是同一个锁!
-
wait和sleep的区别:
wait | sleep | |
---|---|---|
同步 | 只能在同步上下文中调用wait方法,否则或抛出IllegalMonitorStateException异常 | 不需要在同步方法或同步块中调用 |
作用对象 | wait方法定义在Object类中,作用于对象本身 | sleep方法定义在java.lang.Thread中,作用于当前线程 |
释放锁资源 | 是 | 否 |
唤醒条件 | 其他线程调用对象的notify()或者notifyAll()方法 | 超时或者调用interrupt()方法体 |
方法属性 | wait是实例方法 | sleep是静态方法 |