写在前面:
视频是什么东西,有看文档精彩吗?
视频是什么东西,有看文档速度快吗?
视频是什么东西,有看文档效率高吗?
诸小亮:下面我们介绍一些使用线程时常见的问题?
张小飞:都有哪些问题呢?
1. 停止线程
诸小亮:第一,如何停止一个线程
张小飞:这个我知道,Thread类中有一个 stop 方法,可以停止线程
诸小亮:是有一个 stop 方法,不过这个方法已经过时,不推荐使用
张小飞:那还有其他方式吗?
1. 第一种
诸小亮:其实,停止线程的最好方式是让线程正常结束
张小飞:正常结束?能具体的说一说吗?
诸小亮:其实就是,声明一个变量,设置一个开关,比如:
class Hero extends Thread{
//1. 定义一个boolean值,控制线程是否结束
boolean bool;
public void run(){
for (int i = 0; i < 10; i++) {
if(bool){
//2. 当bool=true时,跳出循环,run方法结束,线程也就停止了
break;
}
System.out.println(Thread.currentThread().getName()+"。。。。。。"+i);
try {Thread.sleep(500);} catch (InterruptedException e) {}
}
System.out.println(Thread.currentThread().getName()+"线程结束。。。。。。");
}
}
public class ThreadDemo {
public static void main(String[] args) throws Exception {
Hero hero = new Hero();
hero.start();
Thread.sleep(3000);
System.out.println("main线程休息2秒后,设置bool=true。。。。。。");
hero.bool = true;
}
}
结果:
张小飞:您这不是停止一个线程吧,这不是线程正常跑完 run 方法了吗?
诸小亮:是的,停止线程的最好方式是让线程正常结束
张小飞:原来是这样
2. interrupt
诸小亮:其实 Thread 中有个 interrupt 方法,可以用来停止线程的运行
张小飞:这个方法是???
诸小亮:interrupt,打断正在运行的线程
- 注意:interrupt 并不会终止线程,它只是将线程的中断标记设为true
- 使用 isInterrupted() 方法判断线程的终端标记是否为 true
示例:
class Hero extends Thread{
public void run(){
for (int i = 0; i < 100000000; i++) {
//如果当前线程的中断标记是true,跳出循环,线程结束
if(Thread.currentThread().isInterrupted()){
System.out.println(Thread.currentThread().getName()+"被打断,跳出循环。。。。。。");
break;
}
System.out.println(Thread.currentThread().getName()+"。。。。。。"+i);
}
System.out.println(Thread.currentThread().getName()+"线程结束。。。。。。");
}
}
public class ThreadDemo {
public static void main(String[] args) throws Exception {
Hero hero = new Hero();
hero.start();
Thread.sleep(1000);
System.out.println("main线程休息1秒后,执行interrupt。。。。。。");
hero.interrupt();
}
}
结果:
诸小亮:如果使用 interrupt 方法打断线程,那么就需要调用 isInterrupted 跟它配合使用
张小飞:那,如果run犯法中不调用 isInterrupted 方法呢?
诸小亮:这样的话,线程会一直正常执行,直到 for 循环结束
- 记住:interrupt 并不会终止线程,它只是将线程的中断标记设为true
张小飞:明白了
3. interrupt 和 sleep
诸小亮:使用 interrupt 时,也有一些注意点
张小飞:需要注意哪些地方?
诸小亮:如果线程在 sleep、wait 时被 interrupt会抛出 _InterruptedException,_比如:
结果:
张小飞:这是为什么呢?
诸小亮:你想想,如果你睡觉的时候有人给你一板砖,你会怎么样?
张小飞:。。。。。。
2. 多个线程顺序执行
张小飞:多线程时,也需要顺序执行?
诸小亮:需要看具体场景吧,某些情况下虽然开启了多个线程,但是还是希望他们能个一个接一个的执行
张小飞:但是线程开启后,每个线程都在执行自己的代码,如何让他们顺序执行呢?
诸小亮:Thread中有一个 join 方法,使用它可以让线程顺序执行,比如:
public static void main(String[] args) throws Exception {
//1. 多个线程之间顺序执行
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
System.out.println("线程1执行。。。。。。");
}
});
Thread t2 = new Thread(new Runnable() {
@Override
public void run() {
System.out.println("线程2执行。。。。。。");
}
});
Thread t3 = new Thread(new Runnable() {
@Override
public void run() {
System.out.println("线程3执行。。。。。。");
}
});
t1.start();
t1.join();//执行join方法后,main线程等待t1结束后,才继续往下执行
t2.start();
t2.join();
t3.start();
t3.join();
}
结果:
张小飞:原来是这样,那我觉得这样就没必要开启多线程了
诸小亮:你说的很对,但是面试时有时会就是会这样问,所以记住这个 join 方法就行了
张小飞:好的,好的
诸小亮:另外,一定要注意:t1.join()这句代码是由 main 线程执行的,所以 main 线程会等待
张小飞:等待什么?
诸小亮:。。。。,当然是等待 t1 结束后,在执行 t2.start()
张小飞:哦哦,明白了
3. 守护线程
张小飞:我听说,线程分为守护线程和一般线程,怎么区分它们?
诸小亮:目前我们创建的线程是前台线程,也叫一般线程,线程中还有一种比较特殊的:守护线程
张小飞:那,怎么创建一个守护线程呢?
诸小亮:很简单,通过 setDaemon 方法可以把一个前台线程设置为守护线程
- 比如:setDaemon(true);
张小飞:那,守护线程和一般线程有什么区别呢?
诸小亮:守护线程跟一般线程差不多,只是结束的时有些区别
- 当前台线程结束,守护线程会自动结束
示例:
public static void main(String[] args) throws Exception {
//1. 创建一个线程
Thread yase = new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println(Thread.currentThread().getName()+"。。。。。。"+i);
try {
Thread.sleep(500);
} catch (InterruptedException e) {
}
}
System.out.println("yase线程结束。。。。。。。");
}
},"yase");
//2. 设置yase是守护线程
yase.setDaemon(true);
yase.start();
//3. main是一般线程,休眠3秒后结束
Thread.sleep(3000);
System.out.println("main线程休息3秒后,over。。。。。。");
}
结果:
诸小亮:仔细看上面的输出,发现在 main 线程结束后,yase 这个守护线程也自动结束了
张小飞:原来这就是守护线程
诸小亮:另外,当所有前台线程结束后,守护线程才会结束
张小飞:这是什么意思?
诸小亮:来,看下面的代码
public static void main(String[] args) throws Exception {
//1. 创建一个线程
Thread yase = new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println(Thread.currentThread().getName()+"。。。。。。"+i);
try {
Thread.sleep(500);
} catch (InterruptedException e) {
}
}
System.out.println("yase线程结束。。。。。。。");
}
},"yase");
//2. 设置yase是守护线程
yase.setDaemon(true);
yase.start();
//3. daji是一般线程
Thread daji = new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 20; i++) {
System.out.println(Thread.currentThread().getName()+"-----------------------"+i);
try {
Thread.sleep(500);
} catch (InterruptedException e) {
}
}
System.out.println("daji线程结束。。。。。。。");
}
},"daji");
daji.start();
//4. main是一般线程,休眠3秒后结束
Thread.sleep(3000);
System.out.println("main线程休息3秒后,over。。。。。。");
}
结果:
诸小亮:看到了吧,main线程执行最后一句代码后,yase 和 daji 线程依旧在运行
张小飞:嗯嗯,看到了,不过 yase 这个守护线程什么时候结束呢?
诸小亮:当 daji 线程结束后,程序中就没有一般线程在运行了,所以 yase 这个守护线程也随之结束
张小飞:明白了
4. 线程优先级(了解)
张小飞:我听说,线程是分优先级的,优先级高的获取CPU执行权的概率就大,这是真的吗?
诸小亮:没错,线程确实是分优先级的。通过 getPriority、setPriority 方法可以获取和设置线程的优先级,最大值是 10,最小值是 1。
张小飞:如果不设置呢,它默认的优先级是多少?
诸小亮:如果没有设置,默认优先级是 5。
张小飞:给一段代码演示一下,可以吗?
诸小亮:当然可以了,下面代码中,yase 线程的优先级设置成了 10,而 daji 线程的优先级却被设置成了 1
class Hero extends Thread{
@Override
public void run() {
for (int i = 0; i < 1000000; i++) {
System.out.println(Thread.currentThread().getName()+"。。。。。。"+i);
}
}
}
public class ThreadDemo {
public static void main(String[] args) throws Exception {
Thread yase = new Thread(new Hero(),"yase");
yase.setPriority(10);//设置优先级,10最大,1最小
yase.start();
Thread daji = new Thread(new Hero(), "daji");
daji.setPriority(1);//设置优先级,10最大,1最小
daji.start();
//主线程多睡一会儿
Thread.sleep(60000);
}
}
结果:
诸小亮:注意观察结果,yase线程执行到了54万,daji线程才执行到39万,明显 yase 线程有更高的CPU执行权
5. 线程状态
张小飞:您之前说,线程也是有很多种状态的,那么具体有哪些状态呢?
诸小亮:Thread类中有 State 枚举类,列举了线程状态,一共有 6 种
1. 初始(NEW):新创建了一个线程对象,但还没有调用start()方法
2. 运行(RUNNABLE):Java线程中将就绪(ready)和运行中(running)两种状态统称为“运行”
就绪:线程调用了start()方法后,这时线程位于可运行线程池中,等待获取CPU的使用权
运行:就绪状态的线程在获得CPU使用权后,变为运行中状态(running)
3. 阻塞(BLOCKED):表示线程等待获取锁
遇到 synchronized 变为阻塞状态
4. 等待(WAITING):进入该状态的线程需要等待其他线程做出一些特定动作(唤醒或打断)
遇到 wait、join 变为等待状态
5. 超时等待(TIMED_WAITING):跟WAITING不同,可在指定时间后自己运行
遇到 wait(毫秒数) 变为超时等待
如果在等待时被唤醒,立即执行
等待超时后,先尝试获取锁,不能获取则阻塞,获取到就运行
6. 终止(TERMINATED):表示该线程已经执行完毕
诸小亮:下面这张图可以看出它们各个状态之间的转换
诸小亮:另外,下面表格中是,状态切换时常用方法
start() | 启动一个线程 |
---|---|
run() | 线程需要执行的代码,run方法结束,该线程结束 |
sleep(long millis) | 线程休眠,但不释放锁 |
join() | 等待该线程结束,可以让线程顺序执行 |
wait()/notify()/notifyAll() | wait()使当前线程等待,前提:必须先获得锁,一般配合synchronized 关键字使用 只有当 notify/notifyAll() 被执行时候,才会唤醒该线程继续执行 notify/notifyAll() 的执行只是唤醒等待的线程,而不会立即释放锁,锁的释放要看代码块的具体执行情况。所以尽量在使用了notify/notifyAll() 后立即退出临界区,以唤醒其他线程让其获得锁 |