1. 实现多线程的方法
Oracle 2种
方法一:实现Runnable接口
方法二:继承Thread类
public class RunnableStyle implements Runnable {
public static void main(String[] args) {
System.out.println("用Runnable方法实现线程");
Thread thread = new Thread(new RunnableStyle());
thread.start();
}
@Override
public void run() {
}
}
public class ThreadStyle extends Thread {
public static void main(String[] args) {
new ThreadStyle().start();
}
@Override
public void run() {
System.out.println("用Thread类实现线程");
}
}
- 方法1(实现Runnable接口)更好
- 两种方法的本质对比
方法一:最终调用target.run()
方法二:run()整个都被重写
/**
* 描述: 同时使用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
- 通常我们可以分为两类
- 准确的讲,创建线程只有一种方式那就是构造Thread类,而实现线程的执行单元有两种形式
启动线程的正确和错误方式
一个线程两次调用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.lang.Thread.start(Thread.java:708)
at com.kpioneer.thread.threadcoreknowledge.startthread.CantStartTwice.main(CantStartTwice.java:10)
原因 线程调用start()方法会对线程状态
public synchronized void start() {
if (threadStatus != 0)
throw new IllegalThreadStateException();
2. 如何正确停止线程
原理介绍:使用interrupt来通知,而不是强制
如何正确停止线程
正确的停止方法:interrupt
通常线程会在什么下停止
/**
* 描述: 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.lang.Thread.sleep(Native Method)
at com.kpioneer.thread.threadcoreknowledge.stopthreads.RightWayStopThreadWithSleep.lambda$main$0(RightWayStopThreadWithSleep.java:18)
at java.lang.Thread.run(Thread.java:748)
如果线程在每次迭代后都阻塞
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();
}
}
如果在执行过程中,每次循环都会调用sleep或wait等方法,那么不需要每次迭代都检查是否已中断
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的倍数
java.lang.InterruptedException: sleep interrupted
at java.lang.Thread.sleep(Native Method)
at com.kpioneer.thread.threadcoreknowledge.stopthreads.CantInterrupt.lambda$main$0(CantInterrupt.java:17)
at java.lang.Thread.run(Thread.java:748)
400是100的倍数
500是100的倍数
600是100的倍数
700是100的倍数
800是100的倍数
正确停止带来的好处
实际开发中的两种最佳实践
响应中断的方法总结列表
实际开发中的两种实践
- 优先选择:传递中断
public class RightWayStopThreadInProd implements Runnable {
public static void main(String[] args) throws InterruptedException {
Thread thread = new Thread(new RightWayStopThreadInProd());
thread.start();
Thread.sleep(1000);
thread.interrupt();
}
@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();
}
}
}
/**
* throwInMethod 不能自己try 要抛出异常
*
* @throws InterruptedException
*/
private void throwInMethod() throws InterruptedException {
Thread.sleep(2000);
}
}
go
保存日志
java.lang.InterruptedException: sleep interrupted
at java.lang.Thread.sleep(Native Method)
at com.kpioneer.thread.threadcoreknowledge.stopthreads.RightWayStopThreadInProd.throwInMethod(RightWayStopThreadInProd.java:37)
at com.kpioneer.thread.threadcoreknowledge.stopthreads.RightWayStopThreadInProd.run(RightWayStopThreadInProd.java:21)
at java.lang.Thread.run(Thread.java:748)
- 不想或无法传递:恢复中断
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.lang.InterruptedException: sleep interrupted
at java.lang.Thread.sleep(Native Method)
at com.kpioneer.thread.threadcoreknowledge.stopthreads.RightWayStopThreadInProd2.reInterrupt(RightWayStopThreadInProd2.java:22)
at com.kpioneer.thread.threadcoreknowledge.stopthreads.RightWayStopThreadInProd2.run(RightWayStopThreadInProd2.java:16)
at java.lang.Thread.run(Thread.java:748)
Interrupted,程序运行结束
- 不应屏蔽中断
2. 错误的停止方法
- 被弃用的stop,suspend和resume方法
/**
* 描述: 错误的停止方法:用stop()来停止线程,会导致线程运行一半突然停止,没办法完成一个基本单位的操作(一个连队),会造成脏数据(有的连队多领取少领取装备)。
*/
public class StopThread implements Runnable {
@Override
public void run() {
//模拟指挥军队:一共有5个连队,每个连队10人,以连队为单位,发放武器弹药,叫到号的士兵前去领取
for (int i = 0; i < 5; i++) {
System.out.println("连队" + i + "开始领取武器");
for (int j = 0; j < 10; j++) {
System.out.println(j);
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("连队"+i+"已经领取完毕");
}
}
public static void main(String[] args) {
Thread thread = new Thread(new StopThread());
thread.start();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
thread.stop();
}
}
连队0开始领取武器
0
1
2
3
4
5
6
7
8
9
连队0已经领取完毕
连队1开始领取武器
0
1
2
3
4
5
6
连队要么要么全部领取完 要么一个不领取,但是现在第二个连队 没有全部执行完。
- 用volatile设置boolean标记位
看上去可行
/**
* 描述: 演示用volatile的局限:part1 看似可行
*/
public class WrongWayVolatile implements Runnable {
private volatile boolean canceled = false;
@Override
public void run() {
int num = 0;
try {
while (num <= 100000 && !canceled) {
if (num % 100 == 0) {
System.out.println(num + "是100的倍数。");
}
num++;
Thread.sleep(1);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public static void main(String[] args) throws InterruptedException {
WrongWayVolatile r = new WrongWayVolatile();
Thread thread = new Thread(r);
thread.start();
Thread.sleep(5000);
r.canceled = true;
}
}
- 用volatile设置boolean标记位 无法正常中断生产者消费者模式
/**
* 描述: 演示用volatile的局限part2 陷入阻塞时,volatile是无法线程的 此例中,生产者的生产速度很快,消费者消费速度慢,所以阻塞队列满了以后,生产者会阻塞,等待消费者进一步消费
*/
public class WrongWayVolatileCantStop {
public static void main(String[] args) throws InterruptedException {
ArrayBlockingQueue storage = new ArrayBlockingQueue(10);
Producer producer = new Producer(storage);
Thread producerThread = new Thread(producer);
producerThread.start();
Thread.sleep(1000);
Consumer consumer = new Consumer(storage);
while (consumer.needMoreNums()) {
System.out.println(consumer.storage.take() + "被消费了");
Thread.sleep(100);
}
System.out.println("消费者不需要更多数据了。");
//一旦消费不需要更多数据了,我们应该让生产者也停下来,但是实际情况
producer.canceled = true;
System.out.println(producer.canceled);
}
}
class Producer implements Runnable {
public volatile boolean canceled = false;
BlockingQueue storage;
public Producer(BlockingQueue storage) {
this.storage = storage;
}
@Override
public void run() {
int num = 0;
try {
while (num <= 100000 && !canceled) {
if (num % 100 == 0) {
storage.put(num);
System.out.println(num + "是100的倍数,被放到仓库中了。");
}
num++;
}
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
System.out.println("生产者结束运行");
}
}
}
class Consumer {
BlockingQueue storage;
public Consumer(BlockingQueue storage) {
this.storage = storage;
}
public boolean needMoreNums() {
return !(Math.random() > 0.95);
}
}
0是100的倍数,被放到仓库中了。
100是100的倍数,被放到仓库中了。
200是100的倍数,被放到仓库中了。
300是100的倍数,被放到仓库中了。
400是100的倍数,被放到仓库中了。
500是100的倍数,被放到仓库中了。
600是100的倍数,被放到仓库中了。
700是100的倍数,被放到仓库中了。
800是100的倍数,被放到仓库中了。
900是100的倍数,被放到仓库中了。
0被消费了
1000是100的倍数,被放到仓库中了。
1100是100的倍数,被放到仓库中了。
100被消费了
1200是100的倍数,被放到仓库中了。
200被消费了
300被消费了
1300是100的倍数,被放到仓库中了。
1400是100的倍数,被放到仓库中了。
400被消费了
500被消费了
1500是100的倍数,被放到仓库中了。
600被消费了
1600是100的倍数,被放到仓库中了。
1700是100的倍数,被放到仓库中了。
700被消费了
800被消费了
1800是100的倍数,被放到仓库中了。
900被消费了
1900是100的倍数,被放到仓库中了。
消费者不需要更多数据了。
true
- 修正方式
/**
* 描述: 用中断来修复刚才的无尽等待问题
*/
public class WrongWayVolatileFixed {
public static void main(String[] args) throws InterruptedException {
WrongWayVolatileFixed body = new WrongWayVolatileFixed();
ArrayBlockingQueue storage = new ArrayBlockingQueue(10);
Producer producer = body.new Producer(storage);
Thread producerThread = new Thread(producer);
producerThread.start();
Thread.sleep(1000);
Consumer consumer = body.new Consumer(storage);
while (consumer.needMoreNums()) {
System.out.println(consumer.storage.take() + "被消费了");
Thread.sleep(100);
}
System.out.println("消费者不需要更多数据了。");
producerThread.interrupt();
}
class Producer implements Runnable {
BlockingQueue storage;
public Producer(BlockingQueue storage) {
this.storage = storage;
}
@Override
public void run() {
int num = 0;
try {
while (num <= 100000 && !Thread.currentThread().isInterrupted()) {
if (num % 100 == 0) {
storage.put(num);
System.out.println(num + "是100的倍数,被放到仓库中了。");
}
num++;
}
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
System.out.println("生产者结束运行");
}
}
}
class Consumer {
BlockingQueue storage;
public Consumer(BlockingQueue storage) {
this.storage = storage;
}
public boolean needMoreNums() {
if (Math.random() > 0.95) {
return false;
}
return true;
}
}
}
3.3停止线程相关重要函数解析
/**
* 描述: 注意Thread.interrupted()方法的目标对象是“当前线程”,而不管本方法来自于哪个对象
*/
public class RightWayInterrupted {
public static void main(String[] args) throws InterruptedException {
Thread threadOne = new Thread(new Runnable() {
@Override
public void run() {
for (; ; ) {
//System.out.println("运行中....");
}
}
});
// 启动线程
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.");
}
}
https://blog.csdn.net/qq_39682377/article/details/81449451
https://blog.csdn.net/wyz0923/article/details/106860644
特别感谢
悟空