目录
目录
一.举例示范常见属性
二.休眠一个线程
三.获取线程引用
四.中断一个线程(interrupt)
1. 不执行中断
2.立即执行中断
3. 稍后再执行中断
五. 等待一个线程(join)
六.获取线程的状态 (state)
- ID 是线程的唯一标识,不同线程不会重复
- 名称是各种调试工具用到
- 状态表示线程当前所处的一个情况,下面我们会进一步说明
- 优先级高的线程理论上来说更容易被调度到
- 关于后台线程,需要记住一点:JVM会在一个进程的所有非后台线程结束后,才会结束运行。
- 是否存活,即简单的理解,为 run 方法是否运行结束了
- 是否被中断,重点,下面会详细介绍
程序举例,有以下程序(Thread.currentThread()表示返回当前线程对象的引用)
package thread; public class NINNI { public static void main(String[] args) { //以下是thread线程中的 Thread thread = new Thread(() -> { for (int i = 0; i < 10; i++) { try { System.out.println(Thread.currentThread().getName() + ": 我还活着"); Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println(Thread.currentThread().getName() + ": 我即将死去"); },"迷途知返209"); //以下时Main线程中的 System.out.println(Thread.currentThread().getName() + ": ID: " + thread.getId()); System.out.println(Thread.currentThread().getName() + ": 名称: " + thread.getName()); System.out.println(Thread.currentThread().getName() + ": 状态: " + thread.getState()); System.out.println(Thread.currentThread().getName() + ": 优先级: " + thread.getPriority()); System.out.println(Thread.currentThread().getName() + ": 后台线程: " + thread.isDaemon()); System.out.println(Thread.currentThread().getName() + ": 活着: " + thread.isAlive()); System.out.println(Thread.currentThread().getName() + ": 被中断: " + thread.isInterrupted()); thread.start(); while (thread.isAlive()) {} System.out.println(Thread.currentThread().getName() + ": 状态: " + thread.getState()); } }
代码解读
此代码一共创建了两个线程,一个是main线程,另一个是命名的“迷途知返209”
这两个线程同时执行,当main执行的时候,由于“迷途知返209”线程需要循坏,并且每次循环都要等待1秒,那么肯定main线程中的语句先执行~~~
public class ThreadDemo {
public static void main(String[] args) throws InterruptedException {
System.out.println(System.currentTimeMillis());
Thread.sleep(3 * 1000);
System.out.println(System.currentTimeMillis());
}
}
此时的代码执行到Tread.sleep的时候,会进行休眠,将自己添加到阻塞队列,也就是从CPU上暂时下来。
public class ThreadDemo {
public static void main(String[] args) {
Thread thread = Thread.currentThread();
System.out.println(thread.getName());
}
}
此时就获取到了当前 thread 线程的名字 默认是thread-0;
线程中断的意思大体是这样的:假如 张三有个老婆叫西施 。 我们假设西施是 main线程,张三是另一个t 线程。张三正在执行着 “打游戏 ”的线程。此时西施(main线程)让张三(t线程)停下来去买瓶酱油,那么此时的张三面临着三种选择:
1.放下游戏,立马去买酱油(立即执行中断)
2.不放下游戏,打完最后一波团,再去买酱油(稍后再执行中断)
3.假装没听见,一直打游戏(不执行中断)
那么再代码中我们可以这样写:
public static void main(String[] args) throws InterruptedException { /** * 此处实现进程中断 * 第一种 ↓ * 是main线程告诉t要终止,但是t里面的具体实现是 不执行任何操作 */ Thread t = new Thread(()-> { //interrupt执行后,唤醒sleep,清楚标志位(改为false),循环仍然可以进去,线程不会结束 while (!Thread.currentThread().isInterrupted()) { System.out.println("Hello thread"); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } }); t.start(); Thread.sleep(3000); t.interrupt(); //sleep唤醒,清楚标志位 改为false }
运行结果:
此时程序会一直走,原因如下:
如果看不清该图(劳烦放大~~~)
public static void main(String[] args) throws InterruptedException { /** * 此处实现进程中断 * 第二种 ↓ * 是main线程告诉t要终止,t里面的具体实现是 立刻乖乖听话,停止 */ Thread t = new Thread(()-> { //本来是假,现在取反为真 while (!Thread.currentThread().isInterrupted()) { System.out.println("Hello thread"); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); break; //此处异常之后,立马退出 } } }); t.start(); Thread.sleep(3000); t.interrupt(); }
此处在发生异常之后,直接就break掉了循环,也就是立即终止了进程
运行结果:
public static void main(String[] args) throws InterruptedException { /** * 此处实现进程中断 * 第三种 ↓ * 是main线程告诉t要终止,t里面的具体实现是 我等一会再终止 */ Thread t = new Thread(()-> { //本来是假,现在取反为真 while (!Thread.currentThread().isInterrupted()) { System.out.println("Hello thread"); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); System.out.println("等一等~"); try { Thread.sleep(3000); //进程稍后退出 } catch (InterruptedException ex) { ex.printStackTrace(); } break; } } }); t.start(); Thread.sleep(3000); t.interrupt(); }
此时在执行break之前,又增加了一段Thread.sleep(3000); 代码,也就是t 线程等待3秒之后,再执行break语句;
关于其join相关的方法入下:
其中无参数版本的是 死等 ,必须等到另一个线程结束再罢休。
第二个和第三个 是等待一定的时间,等不到就算了~。
原本线程之间是并发执行的,当引入 join 之后,两个线程就会有一个线程发生阻塞,也就是会有一个线程去等待另一个线程。我们先来看以下代码
package thread; public class ThreadDemo8Join { public static void main(String[] args) { /** * 原本进程之间是并发执行,引入Join之后,当Main线程执行到 Join 后,会先发生阻塞.等待 t 线程执行完 (等小孩放学) * 假设开始执行 Join的时候, t 已经结束了,此时 Join 便不会再阻塞 (孩子已经放学了) * 如果不加Join , 那么 Main 线程 和 t 线程就是抢占式执行方式 */ Thread t = new Thread(()-> { for (int i = 0; i < 3; i++) { System.out.println("我是 t 线程! main线程现在阻塞了,在等我"); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } }); t.start(); System.out.println("Join之前"); // 此处的 join 就是让当前的 main 线程来等待 t 线程结束 try { t.join(); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("Join之后,此时 t 线程已经走完啦"); } }
代码解读:
1.首先我们要清楚,这个代码中有两个线程 Main线程 和 t 线程。
2.当 t.strat()线程启动之后,此时 Main 线程和 t 线程在一起跑
3.当 Main 线程执行到 t.join 的时候,此时 Main 线程阻塞(也即是不能在继续往下走了),此时 t 线程还在继续走,且 t 线程走完之后, Main 线程才可以继续走。
运行结果如下:
线程的状态是一个枚举类型 Thread.State
package thread; public class StateTest { public static void main(String[] args) { for (Thread.State state : Thread.State.values()) { System.out.println(state); } } }
运行结果
线程的状态解释
- NEW: 安排了工作, 还未开始行动
- RUNNABLE: 可工作的. 又可以分成正在工作中和即将开始工作.
- BLOCKED: 这几个都表示排队等着其他事情
- WAITING: 这几个都表示排队等着其他事情
- TIMED_WAITING: 这几个都表示排队等着其他事情
- TERMINATED: 工作完成
这样文字可能不太好理解,我们借助以下例子表示 :
~~~~~~假如你是百万富翁,你让你的两个手下张三和李四去银行取钱~~~~~~
刚把李四、王五找来,还是给他们在安排任务,没让他们行动起来,就是 NEW 状态;
当李四、王五开始去窗口排队,等待服务,就进入到 RUNNABLE 状态。该状态并不表示已经被银行工作人员开始接待,排在队伍中也是属于该状态,即可被服务的状态,是否开始服务,则看调度器的调度;当李四、王五因为一些事情需要去忙,例如需要填写信息、回家取证件、发呆一会等等时,进入BLOCKED 、 WATING 、 TIMED_WAITING 状态,至于这些状态的细分,我们以后再详解;
如果李四、王五已经忙完,为 TERMINATED 状态
代码示例:
package thread; public class ThreadDemo9State { public static void main(String[] args) throws InterruptedException { Thread t = new Thread(()-> { for (int i = 0; i < 100; i++) { try { Thread.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } } }); //启动之前,获取t的状态,就是NEW状态 System.out.println("启动之前 " + t.getState()); t.start(); for (int i = 0; i < 1000; i++) { //通过这里的循环获取,就能够看到这里的交替状态了,具体获取到什么,完全取决于系统里的调度操作 // 如果正在执行-->RUNNABLE 如果正在Sleep ----> TIMED_WAITING; System.out.println("执行之中的状态:" + t.getState()); } t.join(); //执行这个Join , main和t这两个线程就不会并发执行,而是main等待t执行完再执行,也就是t单独执行完 //线程结束之后, 就是 TERMINATED(终止) 状态 System.out.println("启动之后 " + t.getState()); } }
代码解读:
1. t.start() 之前,线程已经准备好了,所以是NEW状态
2. t.start() 之后,此时就有两个线程 main线程 和 t 线程
3. 两个线程同时执行 ,由于 main线程中写的代码少,可能会很快结束,所以加了个循环,此时可以看到 t 线程的状态
4. 假如执行到 t 线程此时正在 sleep ,那么就是TIMED_WAITING
5. 假如执行到 t 线程此时没有sleep , 那么就是RUNNABLE
6. 执行到t.join() 是防止 main线程比 t 线程 提前走完。执行这个Join , main和t这两个线程就不会并发执行,而是main等待 t 执行完再执行,也就是 t 单独执行完。
7. 走到最后,t 一定执行完了,再此查看状态 就是TERMINATED。
本篇博客结束
下回讲解 多线程带来的的风险-线程安全 (重点)~~~