多线程篇二
如笔者理解有误,欢迎交流指正⭐
Thread中重要属性
启动线程start
上篇提到 **start 则是真正调用了系统 API, 在系统内核中创建出线程, 让线程再调用 run.**(“并发“的实现)
再用代码举例下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| class MyThread extends Thread { @Override public void run() { while (true) { System.out.println("Welcome Thread!"); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } } }
public class Demo1 { public static void main(String[] args) throws InterruptedException { Thread thread = new MyThread(); thread.run(); while (true) { System.out.println("Hello Main!"); Thread.sleep(1000); } } }
|
终止(中断)线程
顾名思义,isInterrupted存在的意义就是为了帮助我们停止正在运行的线程.
为什么要中断线程?
作为一个独立的单位,做一件事一定是有目的而且大多结果都是对自己有利,如果局面逐渐不可控制,甚至会造成损害那就有必要让其停下来或者直接取消对应的计划.
在java中我们引入中断的目的就是未了打断线程所处的某种状态(这种状态一定是阻塞状态)
中断的实现
要终止/销毁线程,就是要想办法让run方法尽快执行结束.
手动创建标志位
我们可以在代码中手动创建标志位,作为run执行结束的条件.
上代码套餐.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| public class Demo8 {
public static void main(String[] args) throws InterruptedException { boolean isQuit = false;
Thread thread = new Thread(() -> { while (!isQuit) { System.out.println("线程工作中"); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println("线程工作完毕!"); });
thread.start(); Thread.sleep(3000);
isQuit = true; System.out.println("设置 isQuit 为 true"); } }
|
运行发现
为什么出现这样的情况?
小小lambda 大大的力量😀
isQuit此时是成员变量,而lambda有一个语法规则”变量捕获“.
变量捕获
lambda表达式会把当前作用域中的变量在lambda中cv一份(外面的变量是否销毁也就无关紧要了)
注意:变量捕获的前提是必须只能捕获一个final或者”实际上是final“的变量.(即初始化赋值后未改变值内容的“变量”)
所以isQuit为成员变量时lambda捕获不到,就变成了“内部类访问外部类的属性”.就没有final的限制了.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| public class Demo8 { private static boolean isQuit = false;
public static void main(String[] args) throws InterruptedException { Thread thread = new Thread(() -> { while (!isQuit) { System.out.println("线程工作中"); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println("线程工作完毕!"); });
thread.start(); Thread.sleep(3000);
isQuit = true; System.out.println("设置 isQuit 为 true"); } }
|
缺点
1.需要手动创建变量.
2.当线程内部sleep时,主线程修改变量,新线程不能及时响应.
调用interrupt()
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| public class Demo9 { public static void main(String[] args) { Thread thread = new Thread(() -> { while (!Thread.currentThread().isInterrupted()) { System.out.println("线程工作中"); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } }); thread.start();
try { Thread.sleep(5000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("让 t 线程终止. "); thread.interrupt(); } }
|
currentThread()获取当前线程的实例(Thread thread)
Thread内部有一个标志位用来判断线程是否结束
thread.interrupted()将Thread对象内部的标志位置为true(即使线程内部出现逻辑阻塞【sleep】也可以使用interrupted唤醒【使sleep内部触发一个异常 被提前唤醒】)
运行发现
sleep确实被唤醒了但是线程仍在工作,并未真正结束.
注意:interrupted唤醒线程之后 ,sleep方法抛出异常,同时会自动清楚刚才设置的标志位
有三方式可以让我们有更多的“操作空间”.
上代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| public class Demo9 { public static void main(String[] args) { Thread thread = new Thread(() -> { while (!Thread.currentThread().isInterrupted()) { System.out.println("线程工作中"); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); break; } } }); thread.start();
try { Thread.sleep(5000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("让 t 线程终止. "); thread.interrupt(); } }
|
附:
方法 |
说明 |
public void interrupt() |
中断对象关联的线程,如果线程正在阻塞,则以异常方式通知, 否则设置标志位 |
public static boolean interrupted() |
判断当前线程的中断标志位是否设置,调用后清除标志位 |
public boolean isInterrupted() |
判断对象关联的线程的标志位是否设置,调用后不清除标志位 |
等待进程join
我们知道一个线程在完成它的工作后才能进行自己的下一步工作,但在期间想穿插别的工作进来就需要控制线程结束的顺序来实现.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| public class Demo10 { public static void main(String[] args) throws InterruptedException { Thread thread = new Thread(() -> { for (int i = 0; i < 5; i++) { System.out.println("线程工作中!"); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } }); thread.start();
System.out.println("join 等待开始"); thread.join(); System.out.println("join 等待结束"); } }
|
thread.join工作过程
1.如果线程正在工作中,此时调用thread线程就会阻塞,一直阻塞到thread线程执行结束位置.
2.如果thread线程已经执行结束了,此时调用join线程,就会直接返回,不会涉及阻塞.
【实际开发中比较建议使用join时带有超时时间】
Tips:
sleep本身也有精度误差,调度线程也有开销.(即唤醒线程之后【就绪状态】不是立即回到CPU上运行)
附:
方法 |
说明 |
public void join() |
等待线程结束 |
public void join(long millis) |
等待线程结束,最多等millis毫秒 |
public void join(long millis,int nanos) |
精度更高但同理 |
获取线程引用
方法 |
说明 |
public static Thread currentThread(); |
返回当前线程对象的引用 |
1 2 3 4 5 6
| public class ThreadDemo { public static void main(String[] args) { Thread thread = Thread.currentThread(); System.out.println(thread.getName()); } }
|
休眠进程
眼熟吗老师们hh
注意:因为线程是不可控制的,所以这个方法只能保证实际休眠时间大于等于参数设置的休眠时间
方法 |
说明 |
public static void sleep(long millis) throws InterruptedException |
休眠当前线程 millis毫秒 |
public static void sleep(long millis, int nanos) throws InterruptedException |
更高精度的休眠 |
1 2 3 4 5 6 7
| public class ThreadDemo { public static void main(String[] args) throws InterruptedException { System.out.println(System.currentTimeMillis()); Thread.sleep(3000); System.out.println(System.currentTimeMillis()); } }
|
线程状态
帮助我们快速判断当前程序执行的状况.
线程的状态是一个枚举类型Thread.State.
NEW
安排了工作但还在摸鱼未动
Thread对象已经有了但start方法还没调用.
TERMINATED
工作已完成
Thread对象还在,内核中线程已经结束了.
RUNNABLE
可工作,可以分成正在工作中和即将开始的工作
就绪状态(线程已经在CPU上执行了/线程正在等待CPU执行)
TIMED_WAITING
排队等安排工作
阻塞.sleep这种固定时间的方法产生的阻塞.
WAITING
排队等安排工作
由于wait这种不固定时间的方式产生的阻塞.
BLOCKED
排队等安排工作
阻塞.锁竞争产生.
与阻塞相关的三种状态后续常用于分析“线程卡死”问题
代码套餐
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| public class ThreadStateTransfer { public static void main(String[] args) throws InterruptedException { Thread t = new Thread(() -> { for (int i = 0; i < 1000; i++) { } }, "Thread"); System.out.println(t.getName() + ": " + t.getState()); t.start(); while (t.isAlive()) { System.out.println(t.getName() + ": " + t.getState()); } System.out.println(t.getName() + ": " + t.getState()); } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| public class ThreadStateTransfer { public static void main(String[] args) { final Object object = new Object(); Thread t1 = new Thread(new Runnable() { @Override public void run() { synchronized (object) { while (true) { try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } } } }, "t1"); t1.start(); Thread t2 = new Thread(new Runnable() { @Override public void run() { synchronized (object) { System.out.println("hehe"); } } }, "t2"); t2.start(); } }
|
使用jconsole观察,t1线程为TIMED_WAITING状态(排队等工作 sleep引起),t2线程为BLOCKED状态(由于t1未执行完 产生锁竞争)
修改sleep为wait后 t2线程结束 打印出“hehe” t1线程为WAITING状态
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| public class ThreadStateTransfer { public static void main(String[] args) { final Object object = new Object(); Thread t1 = new Thread(new Runnable() { @Override public void run() { synchronized (object) { while (true) { try { object.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } } } }, "t1"); t1.start(); Thread t2 = new Thread(new Runnable() { @Override public void run() { synchronized (object) { System.out.println("hehe"); } } }, "t2"); t2.start(); } }
|
结论
1.BLOCKED 表示等待获取锁, WAITING 和TIMED_WAITING 表示等待其他线程发来通知. 2.TIMED_WAITING 线程在等待唤醒,但设置了时限; WAITING 线程在无限等待唤醒