1.1、初始线程8大核心基础
2.1、核心1:实现多线程的正确姿势
2.1.1、创建新线程
问题:实现多线程的方法是1种还是2种还是4种?
创建线程2种方式: 1.实现Runanble接口(推荐)->作为参数传入 代码演示:
- /**
- * 描述: 用Runnable方式创建线程
- */
- public class RunnableStyle implements Runnable{
-
- public static void main(String[] args) {
- Thread thread = new Thread(new RunnableStyle());
- thread.start();
- }
-
- @Override
- public void run() {
- System.out.println("用Runnable方法实现线程");
- }
- }
执行结果:
用Runnable方法实现线程
2.继承Thread类 代码演示:
- /**
- * 描述: 用Thread方式实现线程
- */
- public class ThreadStyle extends Thread{
-
- @Override
- public void run() {
- System.out.println("用Thread类实现线程");
- }
-
- public static void main(String[] args) {
- new ThreadStyle().start();
- }
- }
执行结果:
用Thread类实现线程
两种方法对比
方法1(实现Runnable接口)更好。
实现Runnable接口好在哪里?
继承Thread类是不推荐的,因为它有以下的一些缺点:
两种方法的本质对比
方法一和方法二,也就是“实现Runnable接口并传入Thread类”和“继承Thread类然后重写run()”在实现多线程的本质上,并没有区别,都是最终调用了start()方法来新建线程。这两个方法的最主要区别在于run()方法的内容来源:
- @Override
- public void run() {
- if (target != null) {
- target.run();
- }
- }
方法一:最终调用target.run();
方法二:run()整个都被重写
题目描述:有多少种实现线程的方法?典型错误答案和正确答案。(问题选自《常见面试题汇总》)
提示:答题思路,以下5点:
1. 从不同的角度看,会有不同的答案。
2. 典型答案是两种,分别是实现Runnable接口和继承Thread类,然后具体展开说;
3. 但是,我们看原理,其实Thread类实现了Runnable接口,并且看Thread类的run方法,会发现其实那两种本质都是一样的,run方法的代码如下:
@Override
public void run() {
if (target != null) {
target.run();
}
}
方法一和方法二,也就是“继承Thread类然后重写run()”和“实现Runnable接口并传入Thread类”在实现多线程的本质上,并没有区别,都是最终调用了start()方法来新建线程。这两个方法的最主要区别在于run()方法的内容来源:
方法一:最终调用target.run();
方法二:run()整个都被重写
4. 然后具体展开说其他方式:还有其他的实现线程的方法,例如线程池、定时器,它们也能新建线程,但是细看源码,从没有逃出过本质,也就是实现Runnable接口和继承Thread类。
5. 结论:我们只能通过新建Thread类这一种方式来创建线程,但是类里面的run方法有两种方式来实现,第一种是重写run方法,第二种实现Runnable接口的run方法,然后再把该runnable实例传给Thread类。除此之外,从表面上看线程池、定时器等工具类也可以创建线程,但是它们的本质都逃不出刚才所说的范围。
以上这种描述比直接回答一种、两种、多种都更准确。
2.1.2、同时使用两种方法创建线程会怎样?
代码演示:
- /**
- * 描述: 同时使用Runnable和Thread两种实现线程的方式。思路来自于网上的相同思路的面试题。
- */
- public class BothRunnableThread {
-
- public static void main(String[] args) {
- new Thread(new Runnable() {
- @Override
- public void run() {
- System.out.println("我来自Runnable");
- }
- }) {
- @Override
- public void run() {
- System.out.println("我来自Thread");
- }
- }.start();
- }
- }
运行结果:
我来自Thread
2.2、核心2:怎样才是正确的线程启动方式?
2.2.1、启动线程的正确和错误方式
start()和run()的比较
代码演示:
- /**
- * 描述: 对比start和run两种启动线程的方式
- */
- public class StartAndRunMethod {
-
- public static void main(String[] args) {
- Runnable runnable = () -> {
- System.out.println(Thread.currentThread().getName());
-
- };
- runnable.run();// main
-
- new Thread(runnable).start(); // Thread-0
- }
- }
运行结果:
- main
- Thread-0
主线程调用子线程start()方法: 主线程会通知JVM在空闲的时间启动子线程,但启动子线程的时机是由JVM线程调度器来决定的,所以说即使调用了start()方法,也不一定会立即启动子线程,所以调用start线程的顺序,并不能决定线程执行的顺序 子线程的创建准备工作: 1、处于就绪状态(获取到了除了CPU以外的所有资源,如:上下文、栈、线程状态) 做完准备工作,线程才能够被JVM调度到执行状态,等待获取CPU资源,然后才能够真正到运行状态
不能两次调用start方法,否则会报错。
演示:
- /**
- * 描述: 演示不能两次调用start方法,否则会报错
- */
- public class CantStartTwice {
- public static void main(String[] args) {
- Thread thread = new Thread();
- thread.start();
- thread.start();
- }
报错信息:
- Exception in thread "main" java.lang.IllegalThreadStateException
- at java.base/java.lang.Thread.start(Thread.java:793)
- at com.lvxiaosha.threadcoreknowledge.startthread.CantStartTwice.main(CantStartTwice.java:10)
报错原因:线程开始执行,从new状态到后续的状态,线程执行完毕,就会变为终止状态,而终止状态永远不可能会返回回去,所以才会抛出异常。
start方法的执行流程是什么?
注意点:
start方法是被synchronized修饰的方法,可以保证线程安全;
由JVM创建的main方法线程和system组线程,并不会通过start来启动。
run方法经典的3行代码
源码分析
public void run() {
if (target != null) {
target.run();
}
}
两种情况 :
target = null 时候;
直接跳过不执行任何操作;
!null 时候
执行重写run方法(看第二章巩固下)

如果要启动一个线程必须要调用thread的run方法去启动。
面试问题:
启动线程常见面试问题 1、一个线程两次调用start()方法会出现什么情况?为什么? Java的线程是不允许启动两次的,启动第二次会出现IllegalThreadStateException异常,这是一种运行时异常,多次调用start被认为是编程错误。因为在Java5以后,线程的状态会被明确的写入其公共内部枚举类型Java.lang.Thread.State中,分别是:新建,就绪,阻塞,等待、计时等待,终止。在第二次调用start() 方法的时候,已经被start的线程已经不再是(NEW)状态了,所以无论如何是不能再次启动的。
2、既然start()方法会调用run()方法,为什么我们选择调用start()方法,而不是直接掉用run()方法? 调用start方法是真正意义上启动了一个线程,会经历线程的各个生命周期。如果直接调用run()方法,就是一个普通的方法而已,不是使用子线程去调用。
2.3、核心3:如何正确停止线程?
2.3.1、停止线程
原理介绍:使用interrupt来通知,而不是强制停止线程。
Java中停止线程的原则是什么?
在Java中,最好的停止线程的方式是使用中断interrupt,但是这仅仅是会通知到被终止的线程“你该停止运行了”,被终止的线程自身拥有决定权(决定是否、以及何时停止),这依赖于请求停止方和被停止方都遵守一种约定好的编码规范。
任务和线程的启动很容易。在大多数时候,我们都会让它们运行直到结束,或者让它们自行停止。然而,有时候我们希望提前结束任务或线程,或许是因为用户取消了操作,或者服务需要被快速关闭,或者是运行超时或出错了。
要使任务和线程能安全、快速、可靠地停止下来,并不是一件容易的事。Java没有提供任何机制来安全地终止线程。但它提供了中断( Interruption),这是一种协作机制,能够使一个线程终止另一个线程的当前工作。
这种协作式的方法是必要的,我们很少希望某个任务、线程或服务立即停止,因为这种立即停止会使共享的数据结构处于不一致的状态。相反,在编写任务和服务时可以使用一种协作的方式:当需要停止时,它们首先会清除当前正在执行的工作,然后再结束。这提供了更好的灵活性,因为任务本身的代码比发出取消请求的代码更清楚如何执行清除工作。
生命周期结束(End-of-Lifecycle)的问题会使任务、服务以及程序的设计和实现等过程变得复杂,而这个在程序设计中非常重要的要素却经常被忽略。一个在行为良好的软件与勉强运的软件之间的最主要区别就是,行为良好的软件能很完善地处理失败、关闭和取消等过程。
线程停止的情况 1、run方法的代码执行完毕 2、出现异常,并且方法中没有捕获
普通情况下停止线程的代码演示:
- /**
- * 描述: run方法内没有sleep或wait方法时,停止线程
- */
- public class RightWayStopThreadWithoutSleep implements Runnable {
-
- @Override
- public void run() {
- int num = 0;
- while (!Thread.currentThread().isInterrupted() && num <= Integer.MAX_VALUE / 2) {
- if (num % 10000 == 0) {
- System.out.println(num + "是10000的倍数");
- }
- num++;
- }
- System.out.println("任务运行结束了");
- }
-
- public static void main(String[] args) throws InterruptedException {
- Thread thread = new Thread(new RightWayStopThreadWithoutSleep());
- thread.start();
- Thread.sleep(2000);
- thread.interrupt();
- }
- }
阻塞情况下停止线程代码演示:
- /**
- * 描述: 带有sleep的中断线程的写法
- */
- public class RightWayStopThreadWithSleep {
-
- public static void main(String[] args) throws InterruptedException {
- Runnable runnable = () -> {
- int num = 0;
- try {
- while (num <= 300 && !Thread.currentThread().isInterrupted()) {
- if (num % 100 == 0) {
- System.out.println(num + "是100的倍数");
- }
- num++;
- }
- Thread.sleep(1000);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- };
- Thread thread = new Thread(runnable);
- thread.start();
- Thread.sleep(500);
- thread.interrupt();
- }
- }
运行结果:
- 0是100的倍数
- 100是100的倍数
- 200是100的倍数
- 300是100的倍数
- java.lang.InterruptedException: sleep interrupted
- at java.base/java.lang.Thread.sleep(Native Method)
- at com.lvxiaosha.threadcoreknowledge.stopthreads.RightWayStopThreadWithSleep.lambda$main$0(RightWayStopThreadWithSleep.java:18)
- at java.base/java.lang.Thread.run(Thread.java:833)
Thread.sleep时可以感应到别的线程提出的interrupt请求,抛出InterruptedException。
线程在每次迭代后都阻塞的情况停止线程代码演示:
- /**
- * 描述: 如果在执行过程中,每次循环都会调用sleep或wait等方法,那么不需要每次迭代都检查是否已中断
- */
- public class RightWayStopThreadWithSleepEveryLoop {
- public static void main(String[] args) throws InterruptedException {
- Runnable runnable = () -> {
- int num = 0;
- try {
- while (num <= 10000) {
- if (num % 100 == 0) {
- System.out.println(num + "是100的倍数");
- }
- num++;
- Thread.sleep(10);
- }
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- };
- Thread thread = new Thread(runnable);
- thread.start();
- Thread.sleep(5000);
- thread.interrupt();
- }
- }
运行结果:
- 0是100的倍数
- 100是100的倍数
- 200是100的倍数
- 300是100的倍数
- 400是100的倍数
- java.lang.InterruptedException: sleep interrupted
- at java.base/java.lang.Thread.sleep(Native Method)
- at com.lvxiaosha.threadcoreknowledge.stopthreads.RightWayStopThreadWithSleepEveryLoop.lambda$main$0(RightWayStopThreadWithSleepEveryLoop.java:16)
- at java.base/java.lang.Thread.run(Thread.java:833)
每次迭代都会阻塞: 这种情况,线程耗时主要集中在sleep或其他阻塞方法上,其他的操作执行的是很快的。当我们发出一个中断信号后,能够响应这个信号的代码应该就是这个sleep方法,又因为sleep方法可以正确的响应中断,所以在每次循环时就没有必要再检查当前线程是否已经被中断(Thread.currentThreah.isInterrupted())。
在实际业务中,为了保险起见,最好还是while多判断一次。
如果while里面放try/catch,会导致中断失效。
代码演示:
- /**
- * 描述: 如果while里面放try/catch,会导致中断失效
- */
- public class CantInterrupt {
-
- public static void main(String[] args) throws InterruptedException {
- Runnable runnable = () -> {
- int num = 0;
- while (num <= 10000 && !Thread.currentThread().isInterrupted()) {
- if (num % 100 == 0) {
- System.out.println(num + "是100的倍数");
- }
- num++;
- try {
- Thread.sleep(10);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- }
- };
- Thread thread = new Thread(runnable);
- thread.start();
- Thread.sleep(5000);
- thread.interrupt();
- }
- }
运行结果:
- 0是100的倍数
- 100是100的倍数
- 200是100的倍数
- 300是100的倍数
- 400是100的倍数
- java.lang.InterruptedException: sleep interrupted
- at java.base/java.lang.Thread.sleep(Native Method)
- at com.lvxiaosha.threadcoreknowledge.stopthreads.CantInterrupt.lambda$main$0(CantInterrupt.java:17)
- at java.base/java.lang.Thread.run(Thread.java:833)
- 500是100的倍数
- 600是100的倍数
- 700是100的倍数
- 800是100的倍数
可以看到程序没有终止。Java语言在设计sleep方法时,有这样的理念:一旦响应中断,会将线程的interrupt标记位清除。
中断线程的两种最佳实践: 1.传递中断(被线程调用的方法上抛出异常) 2.恢复中断(在被线程调用的方法中可以try/catch异常,但是一定要继续发出相应线程的中断信号,也就是thread.interrupt)
传递中断代码演示:
- /**
- * 描述: 最佳实践:catch了InterruptedExcetion之后的优先选择:在方法签名中抛出异常 那么在run()就会强制try/catch
- * RightWayStopThread相关的类思路代码参考自Java Concurrency In Practice的线程停止相关小节
- */
- public class RightWayStopThreadInProd implements Runnable {
-
- @Override
- public void run() {
- while (true && !Thread.currentThread().isInterrupted()) {
- System.out.println("go");
- try {
- throwInMethod();
- } catch (InterruptedException e) {
- Thread.currentThread().interrupt();
- //保存日志、停止程序
- System.out.println("保存日志");
- e.printStackTrace();
- }
- }
- }
-
- private void throwInMethod() throws InterruptedException {
- Thread.sleep(2000);
- }
-
- public static void main(String[] args) throws InterruptedException {
- Thread thread = new Thread(new RightWayStopThreadInProd());
- thread.start();
- Thread.sleep(1000);
- thread.interrupt();
- }
- }
运行结果:
- go
- 保存日志
- java.lang.InterruptedException: sleep interrupted
- at java.base/java.lang.Thread.sleep(Native Method)
- at com.lvxiaosha.threadcoreknowledge.stopthreads.RightWayStopThreadInProd.throwInMethod(RightWayStopThreadInProd.java:25)
- at com.lvxiaosha.threadcoreknowledge.stopthreads.RightWayStopThreadInProd.run(RightWayStopThreadInProd.java:14)
- at java.base/java.lang.Thread.run(Thread.java:833)
处理中断的最好方法是什么?
优先选择在方法上抛出异常。
用throws InterruptedException 标记你的方法,不采用try 语句块捕获异常,以便于该异常可以传递到顶层,让run方法可以捕获这一异常,例如:
- void subTask() throws InterruptedException
- sleep(delay);
- }
由于run方法内无法抛出checked Exception(只能用try catch),顶层方法必须处理该异常,避免了漏掉或者被吞掉的情况,增强了代码的健壮性。
恢复中断代码演示:
- /**
- * 描述:最佳实践2:在catch子语句中调用Thread.currentThread().interrupt()
- 来恢复设置中断状态,以便于在后续的执行中,依然能够检查到刚才发生了中断
- * 回到刚才RightWayStopThreadInProd补上中断,让它跳出
- */
- public class RightWayStopThreadInProd2 implements Runnable {
-
- @Override
- public void run() {
- while (true) {
- if (Thread.currentThread().isInterrupted()) {
- System.out.println("Interrupted,程序运行结束");
- break;
- }
- reInterrupt();
- }
- }
-
- private void reInterrupt() {
- try {
- Thread.sleep(2000);
- } catch (InterruptedException e) {
- Thread.currentThread().interrupt();
- e.printStackTrace();
- }
- }
-
- public static void main(String[] args) throws InterruptedException {
- Thread thread = new Thread(new RightWayStopThreadInProd2());
- thread.start();
- Thread.sleep(1000);
- thread.interrupt();
- }
- }


Java异常体系

error:程序没有办法处理 Exception:运行时异常(非受检异常)和非运行时异常(受检异常) 受检异常:需要处理,来提高程序的健壮性
错误的线程停止方法
1.stop()强制停止,停止线程会释放当前所拥有的锁,无法保证操作的原子性,可能造成脏数据 2.suspen,resume方法不会放弃当前拥有的锁,线程挂起,可能会一直无法被唤醒或者导致死锁

interrupt状态

static boolean interrupted() ----针对当前运行线程,会清除状态 public boolean isInterrupted()---针对调用线程,不会清除状态 Thread.interrupted() 目标对象是当前执行它的线程,而不是调用它的线程
练习:
- /**
- * 描述: 注意Thread.interrupted()方法的目标对象是“当前线程”,而不管本方法来自于哪个对象。
- * 代码出自Java并发编程之美1.7线程中断,感谢作者翟陆续
- */
- public class RightWayInterrupted {
-
- public static void main(String[] args) throws InterruptedException {
-
- Thread threadOne = new Thread(new Runnable() {
- @Override
- public void run() {
- for (; ; ) {
- }
- }
- });
-
- // 启动线程
- threadOne.start();
- //设置中断标志
- threadOne.interrupt();
- //获取中断标志
- System.out.println("isInterrupted: " + threadOne.isInterrupted());
- //获取中断标志并重置
- System.out.println("isInterrupted: " + threadOne.interrupted());
- //获取中断标志并重直
- System.out.println("isInterrupted: " + Thread.interrupted());
- //获取中断标志
- System.out.println("isInterrupted: " + threadOne.isInterrupted());
- threadOne.join();
- System.out.println("Main thread is over.");
- }
- }
输出结果如下:
- isInterrupted: true
- isInterrupted: false
- isInterrupted: false
- isInterrupted: true
isInterrupted: true --->上一行调用了interrupt,所以这里为true,不会清除这个线程的状态 isInterrupted: false ---> interrupted是静态方法,这里不管看起来是threadOne 这个对象,或者是Thread类来进行调用,其实都是返回主线程的中断标志 isInterrupted: false --->同上第二点 isInterrupted: true 后面没有对threadOne线程的状态进行改变,所以依然会true
常见面试题:
1、如何停止线程?
A. 用volatile的boolean作为标记来停止
B. 用stop()方法让线程停止
C. 用interrupt来请求线程停止
解答:
应该选C。
2、无法响应中断时如何停止线程?
A. 用interrupt方法来请求停止线程
B. 不可中断的阻塞无法处理
C. 根据不同的类调用不同的方法
解答:
应该选C。
如果线程阻塞是由于调用了 wait(),sleep() 或 join() 方法,你可以中断线程,通过抛出 InterruptedException 异常来唤醒该线程。
但是对于不能响应InterruptedException的阻塞,很遗憾,并没有一个通用的解决方案。
但是我们可以利用特定的其它的可以响应中断的方法,比如ReentrantLock.lockInterruptibly(),比如关闭套接字使线程立即返回等方法来达到目的。
答案有很多种,因为有很多原因会造成线程阻塞,所以针对不同情况,唤起的方法也不同。
总结就是说如果不支持响应中断,就要用特定方法来唤起,没有万能药。
2.4、核心4:线程的一生——6个状态(生命周期)
2.4.1、线程状态

线程的6种状态 1、New:已经创建,但还没有启动的新线程(没有执行start方法)。 2、Runnable:一旦调用start方法之后,处于runnable状态。 1)调用start方法之后,线程并不会启动,可能系统没有资源分配,还处于等待状态,那这种情况下时runnable还是waiting呢,其实还是runnbale状态 ,不会从new到waiting状态。 2)线程没有启动,为什么叫runnbale状态呢,runnbale是可运行的,而不是运行中。 3)Java中的runnable对应os中的ready和running。 4)runnbale既可以是可运行的,又可以是正在运行中的。
3、Blocked 1)线程进入被synchronize修饰的代码块之后,并且该锁被其他线程拿走,线程就是blocked状态 2)blocked仅仅是针对synchronized这个关键字 3)和synchronized关键字起到相似效果的还有其他锁,可重入锁、读写锁等都可以让线程进入等待的情况,但是那些情况下不是blocked的线程状态 4、Waiting:等待唤醒 5、Timed Waiting:计时等待(等待超时自动唤醒或者主动唤醒) 6、Terminated:终止状态 1)run方法执行完毕 2)出现异常
线程的NEW、RUNNABLE、Terminated状态代码演示:
- /**
- * 描述: 展示线程的NEW、RUNNABLE、Terminated状态。即使是正在运行,也是Runnable状态,而不是Running。
- */
- public class NewRunnableTerminated implements Runnable {
-
- public static void main(String[] args) {
- Thread thread = new Thread(new NewRunnableTerminated());
- //打印出NEW的状态
- System.out.println(thread.getState());
- thread.start();
- System.out.println(thread.getState());
- try {
- Thread.sleep(10);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- //打印出RUNNABLE的状态,即使是正在运行,也是RUNNABLE,而不是RUNNING
- System.out.println(thread.getState());
- try {
- Thread.sleep(100);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- //打印出TERMINATED状态
- System.out.println(thread.getState());
- }
-
- @Override
- public void run() {
- for (int i = 0; i < 1000; i++) {
- System.out.println(i);
- }
- }
- }
执行结果:
- NEW
- RUNNABLE
- 0
- 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
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
- 99
- 100
- 101
- 102
- 103
- 104
- 105
- 106
- 107
- 108
- 109
- 110
- 111
- 112
- 113
- 114
- 115
- 116
- 117
- 118
- 119
- 120
- 121
- 122
- 123
- 124
- 125
- 126
- 127
- 128
- 129
- 130
- 131
- 132
- 133
- 134
- 135
- 136
- 137
- 138
- 139
- 140
- 141
- 142
- 143
- 144
- 145
- 146
- 147
- 148
- 149
- 150
- 151
- 152
- 153
- 154
- 155
- 156
- 157
- 158
- 159
- 160
- 161
- 162
- 163
- 164
- 165
- 166
- 167
- 168
- 169
- 170
- 171
- 172
- 173
- 174
- 175
- 176
- 177
- 178
- 179
- 180
- 181
- 182
- 183
- 184
- 185
- 186
- 187
- 188
- 189
- 190
- 191
- 192
- 193
- 194
- 195
- 196
- 197
- 198
- 199
- 200
- 201
- 202
- 203
- 204
- 205
- 206
- 207
- 208
- 209
- 210
- 211
- 212
- 213
- 214
- 215
- 216
- 217
- 218
- 219
- 220
- 221
- 222
- 223
- 224
- 225
- 226
- 227
- 228
- 229
- 230
- 231
- 232
- 233
- 234
- 235
- 236
- 237
- 238
- 239
- 240
- 241
- 242
- 243
- 244
- 245
- 246
- 247
- 248
- 249
- 250
- 251
- 252
- 253
- 254
- 255
- 256
- 257
- 258
- 259
- 260
- 261
- 262
- 263
- 264
- 265
- 266
- 267
- 268
- 269
- 270
- 271
- 272
- 273
- 274
- 275
- 276
- 277
- 278
- 279
- 280
- 281
- 282
- 283
- 284
- 285
- 286
- 287
- 288
- 289
- 290
- 291
- 292
- 293
- 294
- 295
- 296
- 297
- 298
- 299
- 300
- 301
- 302
- 303
- 304
- 305
- 306
- 307
- 308
- 309
- 310
- 311
- 312
- 313
- 314
- 315
- 316
- 317
- 318
- 319
- 320
- 321
- 322
- 323
- 324
- 325
- 326
- 327
- 328
- 329
- 330
- 331
- 332
- 333
- 334
- 335
- 336
- 337
- 338
- 339
- 340
- 341
- 342
- 343
- 344
- 345
- 346
- 347
- 348
- 349
- 350
- 351
- 352
- 353
- 354
- 355
- 356
- 357
- 358
- 359
- 360
- 361
- 362
- 363
- 364
- 365
- 366
- 367
- 368
- 369
- 370
- 371
- 372
- 373
- 374
- 375
- 376
- 377
- 378
- 379
- 380
- 381
- 382
- 383
- 384
- 385
- 386
- 387
- 388
- 389
- 390
- 391
- 392
- 393
- 394
- 395
- 396
- 397
- 398
- 399
- 400
- 401
- 402
- 403
- 404
- 405
- 406
- 407
- 408
- 409
- 410
- 411
- 412
- 413
- 414
- 415
- 416
- 417
- 418
- 419
- 420
- 421
- 422
- 423
- 424
- 425
- 426
- 427
- 428
- 429
- 430
- 431
- 432
- 433
- 434
- 435
- 436
- 437
- 438
- 439
- 440
- 441
- 442
- 443
- 444
- 445
- 446
- 447
- 448
- 449
- 450
- 451
- 452
- 453
- 454
- 455
- 456
- 457
- 458
- 459
- 460
- 461
- 462
- 463
- 464
- 465
- 466
- 467
- 468
- 469
- 470
- 471
- 472
- 473
- 474
- 475
- 476
- 477
- 478
- 479
- 480
- 481
- 482
- 483
- 484
- 485
- 486
- 487
- 488
- 489
- 490
- 491
- 492
- 493
- 494
- 495
- 496
- 497
- 498
- 499
- 500
- 501
- 502
- 503
- 504
- 505
- 506
- 507
- 508
- 509
- 510
- 511
- 512
- 513
- 514
- 515
- 516
- 517
- 518
- 519
- 520
- 521
- 522
- 523
- 524
- 525
- 526
- 527
- 528
- 529
- 530
- 531
- 532
- 533
- 534
- 535
- 536
- 537
- 538
- 539
- 540
- 541
- 542
- 543
- 544
- 545
- 546
- 547
- 548
- 549
- 550
- 551
- 552
- 553
- 554
- 555
- 556
- 557
- 558
- 559
- 560
- 561
- 562
- 563
- 564
- 565
- 566
- 567
- 568
- 569
- 570
- 571
- 572
- 573
- 574
- 575
- 576
- 577
- 578
- 579
- 580
- 581
- 582
- 583
- 584
- 585
- 586
- 587
- 588
- 589
- 590
- 591
- 592
- 593
- 594
- 595
- 596
- 597
- 598
- 599
- 600
- 601
- 602
- 603
- 604
- 605
- 606
- 607
- 608
- 609
- 610
- 611
- 612
- 613
- 614
- 615
- 616
- 617
- 618
- 619
- 620
- 621
- 622
- 623
- 624
- 625
- 626
- 627
- 628
- 629
- 630
- 631
- 632
- 633
- 634
- 635
- 636
- 637
- 638
- 639
- 640
- 641
- 642
- 643
- 644
- 645
- 646
- 647
- 648
- 649
- 650
- 651
- 652
- 653
- 654
- 655
- 656
- 657
- 658
- 659
- 660
- 661
- 662
- 663
- 664
- 665
- 666
- RUNNABLE
- 667
- 668
- 669
- 670
- 671
- 672
- 673
- 674
- 675
- 676
- 677
- 678
- 679
- 680
- 681
- 682
- 683
- 684
- 685
- 686
- 687
- 688
- 689
- 690
- 691
- 692
- 693
- 694
- 695
- 696
- 697
- 698
- 699
- 700
- 701
- 702
- 703
- 704
- 705
- 706
- 707
- 708
- 709
- 710
- 711
- 712
- 713
- 714
- 715
- 716
- 717
- 718
- 719
- 720
- 721
- 722
- 723
- 724
- 725
- 726
- 727
- 728
- 729
- 730
- 731
- 732
- 733
- 734
- 735
- 736
- 737
- 738
- 739
- 740
- 741
- 742
- 743
- 744
- 745
- 746
- 747
- 748
- 749
- 750
- 751
- 752
- 753
- 754
- 755
- 756
- 757
- 758
- 759
- 760
- 761
- 762
- 763
- 764
- 765
- 766
- 767
- 768
- 769
- 770
- 771
- 772
- 773
- 774
- 775
- 776
- 777
- 778
- 779
- 780
- 781
- 782
- 783
- 784
- 785
- 786
- 787
- 788
- 789
- 790
- 791
- 792
- 793
- 794
- 795
- 796
- 797
- 798
- 799
- 800
- 801
- 802
- 803
- 804
- 805
- 806
- 807
- 808
- 809
- 810
- 811
- 812
- 813
- 814
- 815
- 816
- 817
- 818
- 819
- 820
- 821
- 822
- 823
- 824
- 825
- 826
- 827
- 828
- 829
- 830
- 831
- 832
- 833
- 834
- 835
- 836
- 837
- 838
- 839
- 840
- 841
- 842
- 843
- 844
- 845
- 846
- 847
- 848
- 849
- 850
- 851
- 852
- 853
- 854
- 855
- 856
- 857
- 858
- 859
- 860
- 861
- 862
- 863
- 864
- 865
- 866
- 867
- 868
- 869
- 870
- 871
- 872
- 873
- 874
- 875
- 876
- 877
- 878
- 879
- 880
- 881
- 882
- 883
- 884
- 885
- 886
- 887
- 888
- 889
- 890
- 891
- 892
- 893
- 894
- 895
- 896
- 897
- 898
- 899
- 900
- 901
- 902
- 903
- 904
- 905
- 906
- 907
- 908
- 909
- 910
- 911
- 912
- 913
- 914
- 915
- 916
- 917
- 918
- 919
- 920
- 921
- 922
- 923
- 924
- 925
- 926
- 927
- 928
- 929
- 930
- 931
- 932
- 933
- 934
- 935
- 936
- 937
- 938
- 939
- 940
- 941
- 942
- 943
- 944
- 945
- 946
- 947
- 948
- 949
- 950
- 951
- 952
- 953
- 954
- 955
- 956
- 957
- 958
- 959
- 960
- 961
- 962
- 963
- 964
- 965
- 966
- 967
- 968
- 969
- 970
- 971
- 972
- 973
- 974
- 975
- 976
- 977
- 978
- 979
- 980
- 981
- 982
- 983
- 984
- 985
- 986
- 987
- 988
- 989
- 990
- 991
- 992
- 993
- 994
- 995
- 996
- 997
- 998
- 999
- TERMINATED
展示Blocked, Waiting, TimedWaiting线程状态代码演示:
- /**
- * 描述: 展示Blocked, Waiting, TimedWaiting
- */
- public class BlockedWaitingTimedWaiting implements Runnable{
- public static void main(String[] args) {
- BlockedWaitingTimedWaiting runnable = new BlockedWaitingTimedWaiting();
- Thread thread1 = new Thread(runnable);
- thread1.start();
- Thread thread2 = new Thread(runnable);
- thread2.start();
- try {
- Thread.sleep(5);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- //打印出Timed_Waiting状态,因为正在执行Thread.sleep(1000);
- System.out.println(thread1.getState());
- //打印出BLOCKED状态,因为thread2想拿得到sync()的锁却拿不到
- System.out.println(thread2.getState());
- try {
- Thread.sleep(1300);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- //打印出WAITING状态,因为执行了wait()
- System.out.println(thread1.getState());
-
- }
-
- @Override
- public void run() {
- syn();
- }
-
- private synchronized void syn() {
- try {
- Thread.sleep(1000);
- wait();
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- }
- }
运行结果:
- TIMED_WAITING
- BLOCKED
- WAITING
2.4.2、阻塞状态

2.4.3、面试题
线程有哪几种状态?生命周期是什么?
2.5、核心5:Thread和Object类中的重要方法详解
主要方法
1、为什么线程通信的方法wait(),notify()和notifyAll()被定义在Object类里面?而sleep定义在了Thread类中? 2、用3中方式实现生产者模式 3、java se 8 和java 1.8 和jdk 8是什么关系,是同一个东西吗? 4、join和sleep和wait期间线程的状态分别是什么?为什么?
2.5.1、wait方法

阻塞阶段 wait 一个线程或者多个线程暂时休息,等到后续需要时再唤醒。 执行wait方法时,必须先拥有对象的monitor锁,调用wait方法之后,调用这个方法的线程进入阻塞状态,不参与线程的调度,直到以下4种情况之一发生时,才会被唤醒。 1)另一个线程调用这个对象的notify()方法且刚好被唤醒的是本线程(notify只唤醒一个线程) 2)另外一个线程调用这个对象的notifyAll()方法。 3)过了wait(long timeout)规定的超时时间,如果传入为0就是永久等待。 4)线程自身调用了interrupt()。
唤醒阶段 notify:唤醒单个正在等待某对象monitor的线程,唤醒时,如果有多个线程都在等待,选择其中一个唤醒(具体的唤醒选择任意的,Java对此没有一个严格的规范,JVM可以拥有自己的实现)。 notify和wait都需要在synchronized关键字保护的方法或者代码块中执行,在外面执行的话,会抛出异常 notifyAll:唤醒所有等待的线程。
遇到中断 假设线程执行了wait方法,在此期间如果被中断,会抛出InterruptedException,释放掉已经获取到的monitor。
wait和notify的基本用法代码演示:
- /**
- * 描述: 展示wait和notify的基本用法
- * 1. 研究代码执行顺序
- * 2. 证明wait释放锁
- */
- public class Wait {
-
- public static Object object = new Object();
-
- static class Thread1 extends Thread {
-
- @Override
- public void run() {
- synchronized (object) {
- System.out.println(Thread.currentThread().getName() + "开始执行了");
- try {
- object.wait();
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- System.out.println("线程" + Thread.currentThread().getName() + "获取到了锁。");
- }
- }
- }
-
- static class Thread2 extends Thread {
-
- @Override
- public void run() {
- synchronized (object) {
- object.notify();
- System.out.println("线程" + Thread.currentThread().getName() + "调用了notify()");
- }
- }
- }
-
- public static void main(String[] args) throws InterruptedException {
- Thread1 thread1 = new Thread1();
- Thread2 thread2 = new Thread2();
- thread1.start();
- Thread.sleep(200);
- thread2.start();
- }
- }
运行结果:
- Thread-0开始执行了
- 线程Thread-1调用了notify()
- 线程Thread-0获取到了锁。
代码演示:3个线程,线程1和线程2首先被阻塞,线程3唤醒它们。notify, notifyAll。 start先执行不代表线程先启动。
- /**
- * 描述: 3个线程,线程1和线程2首先被阻塞,线程3唤醒它们。
- * notify, notifyAll。
- * start先执行不代表线程先启动。
- */
- public class WaitNotifyAll implements Runnable {
-
- private static final Object resourceA = new Object();
-
-
- public static void main(String[] args) throws InterruptedException {
- Runnable r = new WaitNotifyAll();
- Thread threadA = new Thread(r);
- Thread threadB = new Thread(r);
- Thread threadC = new Thread(new Runnable() {
- @Override
- public void run() {
- synchronized (resourceA) {
- resourceA.notifyAll();
- // resourceA.notify();
- System.out.println("ThreadC notified.");
- }
- }
- });
- threadA.start();
- threadB.start();
- Thread.sleep(200);
- threadC.start();
- }
- @Override
- public void run() {
- synchronized (resourceA) {
- System.out.println(Thread.currentThread().getName()+" got resourceA lock.");
- try {
- System.out.println(Thread.currentThread().getName()+" waits to start.");
- resourceA.wait();
- System.out.println(Thread.currentThread().getName()+"'s waiting to end.");
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- }
- }
- }
运行结果:
- Thread-0 got resourceA lock.
- Thread-0 waits to start.
- Thread-1 got resourceA lock.
- Thread-1 waits to start.
- ThreadC notified.
- Thread-0's waiting to end.
- Thread-1's waiting to end.
- /**
- * 描述: 3个线程,线程1和线程2首先被阻塞,线程3唤醒它们。
- * notify, notifyAll。
- * start先执行不代表线程先启动。
- */
- public class WaitNotifyAll implements Runnable {
-
- private static final Object resourceA = new Object();
-
-
- public static void main(String[] args) throws InterruptedException {
- Runnable r = new WaitNotifyAll();
- Thread threadA = new Thread(r);
- Thread threadB = new Thread(r);
- Thread threadC = new Thread(new Runnable() {
- @Override
- public void run() {
- synchronized (resourceA) {
- // resourceA.notifyAll();
- resourceA.notify();
- System.out.println("ThreadC notified.");
- }
- }
- });
- threadA.start();
- threadB.start();
- Thread.sleep(200);
- threadC.start();
- }
- @Override
- public void run() {
- synchronized (resourceA) {
- System.out.println(Thread.currentThread().getName()+" got resourceA lock.");
- try {
- System.out.println(Thread.currentThread().getName()+" waits to start.");
- resourceA.wait();
- System.out.println(Thread.currentThread().getName()+"'s waiting to end.");
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- }
- }
- }
运行结果:
- Thread-0 got resourceA lock.
- Thread-0 waits to start.
- ThreadC notified.
- Thread-1 got resourceA lock.
- Thread-1 waits to start.
- Thread-0's waiting to end.
wait只释放当前的那把锁,不同的锁之间相互独立。
代码演示:
- /**
- * 描述: 证明wait只释放当前的那把锁
- */
- public class WaitNotifyReleaseOwnMonitor {
-
- private static volatile Object resourceA = new Object();
- private static volatile Object resourceB = new Object();
-
- public static void main(String[] args) {
- Thread thread1 = new Thread(new Runnable() {
- @Override
- public void run() {
- synchronized (resourceA) {
- System.out.println("ThreadA got resourceA lock.");
- synchronized (resourceB) {
- System.out.println("ThreadA got resourceB lock.");
- try {
- System.out.println("ThreadA releases resourceA lock.");
- resourceA.wait();
-
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- }
- }
- }
- });
-
- Thread thread2 = new Thread(new Runnable() {
- @Override
- public void run() {
- try {
- Thread.sleep(1000);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- synchronized (resourceA) {
- System.out.println("ThreadB got resourceA lock.");
- System.out.println("ThreadB tries to resourceB lock.");
-
- synchronized (resourceB) {
- System.out.println("ThreadB got resourceB lock.");
- }
- }
- }
- });
-
- thread1.start();
- thread2.start();
- }
- }
运行结果:
- ThreadA got resourceA lock.
- ThreadA got resourceB lock.
- ThreadA releases resourceA lock.
- ThreadB got resourceA lock.
- ThreadB tries to resourceB lock.
wait、notify、notifyAll特点、性质 1、必须先拥有monitor 2、notify只能唤醒一个 ,唤醒哪个线程取决于JVM的实现 3、属于Object类 4、类似功能的Condition 5、同时拥有多个锁的情况(只会释放现在wait所对应的对象的那把锁,如果对象持有多把锁,注意释放顺序,否则会发生死锁)



重点:使用wait/notify实现生产者消费者设计模式


代码演示:
- import java.util.Date;
- import java.util.LinkedList;
-
- /**
- * 描述: 用wait/notify来实现生产者消费者模式
- * 思路参考自Java concurrency Cookbook Using conditions in synchronized code
- */
- public class ProducerConsumerModel {
- public static void main(String[] args) {
- EventStorage eventStorage = new EventStorage();
- Producer producer = new Producer(eventStorage);
- Consumer consumer = new Consumer(eventStorage);
- new Thread(producer).start();
- new Thread(consumer).start();
- }
- }
-
- class Producer implements Runnable {
-
- private EventStorage storage;
-
- public Producer(
- EventStorage storage) {
- this.storage = storage;
- }
-
- @Override
- public void run() {
- for (int i = 0; i < 100; i++) {
- storage.put();
- }
- }
- }
-
- class Consumer implements Runnable {
-
- private EventStorage storage;
-
- public Consumer(
- EventStorage storage) {
- this.storage = storage;
- }
-
- @Override
- public void run() {
- for (int i = 0; i < 100; i++) {
- storage.take();
- }
- }
- }
-
- class EventStorage {
-
- private int maxSize;
- private LinkedList
storage; -
- public EventStorage() {
- maxSize = 10;
- storage = new LinkedList<>();
- }
-
- public synchronized void put() {
- while (storage.size() == maxSize) {
- try {
- wait();
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- }
- storage.add(new Date());
- System.out.println("仓库里有了" + storage.size() + "个产品。");
- notify();
- }
-
- public synchronized void take() {
- while (storage.size() == 0) {
- try {
- wait();
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- }
- System.out.println("拿到了" + storage.poll() + ",现在仓库还剩下" + storage.size());
- notify();
- }
- }