一、线程的生命周期
没时间画图了,直接把书上的图拍下来。
线程由图可知,大体分为5个主要的阶段:
- NEW
- RUNNABLE
- RUNNING
- BLOCKED
- TERMINATED
1.线程的NEW状态
当我们用关键字new创建一个Thread对象时,此时它并不处于执行状态,因为没有调start方法启动该线程,那么线程的状态为NEW状态。
2.线程的RUNNABLE状态
线程对象进入RUNNABLE状态必须调用start方法,那么此时才是真正的在JVM进程中创建了一个线程,此时并不是立即得到执行,线程的运行与否跟进程一样要听令于CPU的调度,这个中间状态称为可执行状态(RUNNABLE),它只是具备了可执行的资格,但并没有真正的执行起来,需要等待CPU的调度。
RUNNABLE的线程只能意外终止或者进入RUNNING状态。
3.线程的RUNNING状态
一旦CPU通过轮询或者其他方式从任务可执行队列中选中了线程,那么此时的它才是真正的执行自己的逻辑代码,RUNNING状态的线程事实上也是RUNNABLE的,反过来不成立。
4.线程的BLOCKED状态
RUNNING状态的线程进行某个阻塞的IO操作,比如因网络的读写而进入了BLOCKED状态;或者RUNNING状态的线程获取某个锁资源,从而加入到该锁的阻塞队列中而进入BLOCKED状态。
5.线程的TERMINATED状态
TERMINATED是线程的最终状态,在该状态中线程将不会切换到其他任何状态,线程进入TERMINATED状态,意味着该线程的整个生命周期都结束了。线程进入TERMINATED状态有三种情况:
- 线程运行正常结束,结束生命周期
- 线程运行出错意外结束
- JVM Crash,导致所有的线程都结束
二、模版设计模式、策略模式
略。。。。
三、Thread模拟营业厅大厅叫号机程序
创建线程只有一种方式,那就是构建Thread类,而实现线程的执行单元有两种方式,第一种是重写Thread类的run方法,第二种是实现Runnable接口的run方法,并且将Runnable实例用作构造Thread的参数。他们之间一个很重要的不同,那就是Thread类的run方法是不能共享的,也就说A线程不能把B线程的run方法当作自己的执行单元,而使用Runnable接口则很容易能实现这一点,使用同一个Runnable的实例构造不同的Thread实例。
例如,为了使用同一个Runnable的实例,共享index变量,而实现对营业厅叫号机程序的模拟。(存在线程安全问题)
简单代码示例:
package com.jseeker.thread;
public class TicketWindowRunable implements Runnable {
private int index = 1;
private final static int MAX = 50;
@Override
public void run() {
while (index <= MAX) {
System.out.println(Thread.currentThread() + " 的号码是:" + (index++));
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
final TicketWindowRunable task = new TicketWindowRunable();
Thread windowThead1 = new Thread(task, "一号窗口");
Thread windowThead2 = new Thread(task, "二号窗口");
Thread windowThead3 = new Thread(task, "三号窗口");
Thread windowThead4 = new Thread(task, "四号窗口");
windowThead1.start();
windowThead2.start();
windowThead3.start();
windowThead4.start();
}
}
注:该程序的共享资源存在线程安全问题,多运行几次或者增加MAX的值可出现,此程序只能作为认识Thread程序用。