一、概念
一个线程对象在它的生命周期内,需要经历5个状态。
▪ 新生状态(New)
用new关键字建立一个线程对象后,该线程对象就处于新生状态。处于新生状态的线程有自己的内存空间,通过调用start方法进入就绪状态。
▪ 就绪状态(Runnable)
处于就绪状态的线程已经具备了运行条件,但是还没有被分配到CPU,处于“线程就绪队列”,等待系统为其分配CPU。就绪状态并不是执行状态,
当系统选定一个等待执行的Thread对象后,它就会进入执行状态。一旦获得CPU,线程就进入运行状态并自动调用自己的run方法。
有4个原因会导致线程进入就绪状态:
1. 新建线程:调用start()方法,进入就绪状态;
2. 阻塞线程:阻塞解除,进入就绪状态;
3. 运行线程:调用yield()方法,直接进入就绪状态;
4. 运行线程:JVM将CPU资源从本线程切换到其他线程。
【yield()方法】
/**就绪状态(Runnable) yield 放弃,屈服 * 2-3、运行线程:调用yield()方法,直接进入就绪状态; yield 礼让(cpu的调度给别的)线程,暂停线程 * 让当前正在执行的线程暂停不是阻塞线程,而是将线程从运行状态转入就绪状态;让cpu重新调度 * * */ package cn.sxt.thread; public class Test_0407_ThreadRunnable_yield { static class Test_yield implements Runnable{ //线程入口入口点 public void run() { for (int i = 1; i <100; i++) { System.out.println("静态内部类线程-->>"+i); } } } public static void main(String[] args) { new Thread(new Test_yield()).start(); for (int i = 1; i <= 100; i++) { if (i%5==0) { //是5的倍数,主方法线程礼让一次 .有的时候礼让并不一定成功 Thread.yield(); } System.out.println("主方法线程-->>"+i); } } }
▪ 运行状态(Running)
在运行状态的线程执行自己run方法中的代码,直到调用其他方法而终止或等待某资源而阻塞或完成任务而死亡。如果在给定的时间片内没有执行结束,
就会被系统给换下来回到就绪状态。也可能由于某些“导致阻塞的事件”而进入阻塞状态。
▪ 阻塞状态(Blocked)
阻塞指的是暂停一个线程的执行以等待某个条件发生(如某资源就绪)。有4种原因会导致阻塞:
1. 执行sleep(int millsecond)方法,使当前线程休眠,进入阻塞状态。当指定的时间到了后,线程进入就绪状态。抱着资源睡觉不给别人用。
2. 执行wait()方法,使当前线程进入阻塞状态。当使用nofity()方法唤醒这个线程后,它进入就绪状态。等着别人用完资源。
3. 线程运行时,某个操作进入阻塞状态,比如执行IO流操作(read()/write()方法本身就是阻塞的方法)。只有当引起该操作阻塞的原因消失后,线程进入就绪状态。
4. join()线程联合: 当某个线程等待另一个线程执行结束后,才能继续执行时,使用join()方法。有人插队。
【sleep()方法】
/***线程阻塞状态 * 3-1:sleep() 方法 。抱着资源睡觉。模拟网络延时,放大发生问题的可能性,便于解决问题如12306,有数据错误 * 龟兔赛跑,模拟兔子睡觉 ;可以写倒计时 * * */ package cn.sxt.thread; import java.text.SimpleDateFormat; import java.util.Date; public class Test_0407_ThreadBlocked_sleep { public static void main(String[] args) throws InterruptedException { /*//倒数10个数 1秒钟一个数。这里是死循环。哪里写sleep哪个线程就睡觉,进行阻塞,这里写在主方法里 int num=10; while (true) { Thread.sleep(1000); //主方法线程要阻塞 System.out.println(num--); } */ //倒计时 endDate对象的初始值是当前时间加10秒后,从下文看是个变量 Date endDate=new Date(System.currentTimeMillis()+1000*10); //电脑系统当前时间+10秒=最终电脑系统线要终止的时刻 System.out.println(new SimpleDateFormat("yy:mm:ss").format(System.currentTimeMillis())+"---->"); long end=endDate.getTime();//end是个定值表示10秒后的时刻 System.out.println(new SimpleDateFormat("yy:mm:ss").format(end)+"---->"); while (true) { System.out.println(new SimpleDateFormat("yy:mm:ss").format(endDate));//格式化打印时间 Thread.sleep(1000);//getTime()得到对象的时间 endDate=new Date(endDate.getTime()-1000);//10秒后的时间开始自减直到减到当前时间 if (end-10000>endDate.getTime()) { //endDate.getTime()一直在变小,当小于临界值系统当前的时刻时,跳出循环 break; } } } }
【join()方法】
/** 3-4 join()线程联合: 当某个线程等待另一个线程执行结束后,才能继续执行时,使用join()方法。有人插队。 *其它线程必须让新插入的线程执行完毕才能继续执行。 * *线程A在运行期间,可以调用线程B的join()方法,让线程B和线程A联合。这样,线程A就必须等待线程B执行完毕后,才能继续执行。 *如下面示例中,“爸爸线程”要抽烟,于是联合了“儿子线程”去买烟,必须等待“儿子线程”买烟完毕,“爸爸线程”才能继续抽烟。 * */ package cn.sxt.thread; public class Test_0407_ThreadBlocked_join { public static void main(String[] args) { System.out.println("爸爸和儿子买烟故事"); /*Thread father = new Thread(new FatherThread()); father.start();*/ new Thread(new FatherThread()).start(); } } class FatherThread implements Runnable { public void run() { System.out.println("爸爸想抽烟,发现烟抽完了"); System.out.println("爸爸让儿子去买包红塔山"); //父亲线程中新插入儿子的线程 Thread son = new Thread(new SonThread()); son.start(); System.out.println("爸爸等儿子买烟回来"); try { son.join();//join写在父亲线程中,所以父亲线程被阻塞,必须等儿子线程执行完他才开始执行 } catch (InterruptedException e) { e.printStackTrace(); System.out.println("爸爸出门去找儿子跑哪去了"); // 结束JVM。如果是0则表示正常结束;如果是非0则表示非正常结束 System.exit(1); } System.out.println("爸爸高兴的接过烟开始抽,并把剩下的零钱给了儿子"); } } class SonThread implements Runnable { public void run() { System.out.println("儿子出门去买烟"); System.out.println("儿子买烟需要10分钟"); try { for (int i = 1; i <= 10; i++) { System.out.println("第" + i + "分钟"); Thread.sleep(1000); } } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("儿子买烟回来了"); } }
▪ 死亡状态(Terminated)
死亡状态是线程生命周期中的最后一个阶段。线程死亡的原因有两个。一个是正常运行的线程完成了它run()方法内的全部工作; 另一个是线程被强制终止,
如通过执行stop()或destroy()方法来终止一个线程(注:stop()/destroy()方法已经被JDK废弃,不推荐使用)。
当一个线程进入死亡状态以后,就不能再回到其它状态了。
终止线程我们一般不使用JDK提供的stop()/destroy()方法(它们本身也被JDK废弃了)。通常的做法是提供一个boolean型的终止变量,
当这个变量置为false,则终止线程的运行。
/*** * 终止线程2种方式: * 1、线程正常执行完毕->线程有次数的限制 * 2、外部终止 加入标志位 */ package cn.sxt.thread; import java.util.jar.Attributes.Name; public class Test_0407_ThreadStop implements Runnable { private boolean flag =true;//1、设置线程标志位 private String name; public Test_0407_ThreadStop(String name) {//构造器 super(); this.name = name; } public void run() {//2、关联标志位 true:线程继续运行 false:线程终止 int i=0; while (flag) { System.out.println(name+"->>"+(i++)); } } //3、对外提供标识去改变标识 public void stop() { this.flag=false; } public static void main(String[] args) { Test_0407_ThreadStop tt=new Test_0407_ThreadStop("C罗"); new Thread(tt).start();//new一个对象进入 新生状态 ;通过start()方法进去就绪状态;cpu调度到了,进入运行状态(人为不能干预) for (int i = 0; i < 10; i++) { if (i==8) { tt.stop();//线程的终止,死亡状态 System.out.println("比赛结束"); } System.out.println("主方法-->>"+i); } } }
二、获取线程基本信息
【代码】
/** *线程的一些方法 * */ package cn.sxt.thread; public class Test_0407_ThreadStates { public static void main(String[] args) throws Exception { System.out.println(Thread.currentThread().isAlive()); MyThread myThread = new MyThread("战斗机");//真实类 ,角色对象,名字通过面向对象思维设置 Thread t=new Thread(myThread);//代理类,代理对象 t.setName("公鸡");//默认代理Thread类t对象的名字是Thread-0,1,2,3..... t.start(); Thread.sleep(500); System.out.println(t.isAlive());//判断线程还在运行吗? 延时500毫秒,显然线程已经死亡,输出false /* Thread t = new Thread(r, "C罗");//定义线程对象,并传入参数; t.start();//启动线程; System.out.println("name is: " + t.getName());//输出线程名称; System.out.println(t.getPriority());//获得线程的优先级 t.sleep(5000); //Thread.currentThread().sleep(5000);//当前线程暂停5秒; */ } } //设置名字:代理角色(jar包中的Thread类)的名字+代理角色(jar包中的Thread类)的名字 class MyThread implements Runnable { private String name; public MyThread(String name) { super(); this.name = name; } //线程体; public void run() {//输出代理角色(jar包中的Thread类)的名字+代理角色(jar包中的Thread类)的名字 System.out.println(Thread.currentThread().getName()+"-->"+name); } }