控制线程(使用以下4种方式可以对线程的运行方式进行控制)
- join线程
- 后台线程
- 线程睡眠
- 改变现成的优先级
1、join线程
Thread提供了让一个线程等待另一个线程完成的方法--join()方法,也就是一个线程中调用其他线程的join()方法时,调用线程被阻塞,直到join线程执行完毕后再次启动该调用线程。话不多说,看代码:
package com.ly.thread;
/**
* liyang 2020-08-11
*/
public class JoinThread extends Thread {
public JoinThread(String name) {
super(name); //用于给该线程设置名字
}
@Override
public void run() {
for(int i = 0; i < 100; i++) {
System.out.println(getName() + " " + i);
}
}
public static void main(String[] args) throws InterruptedException {
new JoinThread("Thread-01").start();
for(int i = 0; i < 100; i++) {
if(i == 20) {
JoinThread jt = new JoinThread("Thread-02-join");
jt.start();
jt.join();
}
System.out.println(Thread.currentThread().getName() + " " + i);
}
}
}
程序说明:
首先创建一个主线程main,接着创建一个子线程Thread-01,当主线程运行一段时间后创建一个join子线程Thread-02-join,此时连个子线程交替执行,main线程等待Thread-02-join子线程执行完毕。
运行结果:
由于运行结果太长,这里给个部分截图,从中可以看出,主线程等待join线程执行完毕后继续执行,看红框部分的数字就能明白。
2、后台线程(Daemon Thread)
在后台运行,它的存在就是为其他线程提供服务,又名“守护线程”或“精灵线程”,JVM的垃圾回收线程就是典型的后台线程。除了后台线程外的线程统一被称为用户线程,虚拟机必须确保用户线程执行完毕,对于后台线程,很明显,不用等待其运行完毕。对于程序中手动创建的线程,默认都不是守护线程,所以需要自行设置,使用函数t.setDaemon(true)即可。
后台线程有个特征:如果所有的前台线程都死亡,后台线程也会死亡。
判断一个线程是否是后台线程,使用t.isDaemon()方法。
package com.ly.thread;
/**
* liyang 2020-08-11
*/
public class DaemonThread extends Thread{
@Override
public void run() {
for(int i = 0; i < 1000; i++) {
System.out.println(getName() + " " + i);
}
}
public static void main(String[] args) {
DaemonThread dt = new DaemonThread();
dt.setDaemon(true);
dt.start();
for(int i = 0; i < 5; i++) {
System.out.println(Thread.currentThread().getName() + " " + i);
}
//至此,用户线程运行结束
//后台线程也应该随之结束
}
}
运行结果:见下图,每次运行的结果具有随机性,但是都能体现出后台线程的特征。
main 0
main 1
main 2
main 3
main 4
Thread-0 0
Thread-0 1
Thread-0 2
Thread-0 3
Thread-0 4
Thread-0 5
Thread-0 6
Process finished with exit code 0
3、线程睡眠:sleep
sleep()方法是Thread类的静态方法,调用后该线程进入阻塞状态,即使当前cpu闲置,也要等到睡眠时间过去。
package com.ly.thread;
import java.util.Date;
/**
* liyang 2020-08-11
*/
public class SleepTest {
public static void main(String[] args) throws InterruptedException {
for(int i = 0; i < 5; i++) {
System.out.println("Current Time: " + new Date());
Thread.sleep(1000);
}
}
}
运行结果:
Current Time: Tue Aug 11 16:41:01 CST 2020
Current Time: Tue Aug 11 16:41:02 CST 2020
Current Time: Tue Aug 11 16:41:03 CST 2020
Current Time: Tue Aug 11 16:41:04 CST 2020
Current Time: Tue Aug 11 16:41:05 CST 2020
Process finished with exit code 0
Thread类中还有一个静态方法yield()与sleep()类似,对比如下:
sleep方法 | yeild方法 | |
1 | sleep()方法暂停当前线程后,会给其他线程执行机会,不会理会其他线程的优先级 | yeild礼让一下,只给优先级相同或者优先级大于当前线程的且处于就绪状态的线程执行的机会 |
2 | 转入阻塞状态,直到睡眠时间结束重新回到就绪状态 | 强制进入就绪状态,当其优先级最高时,完全可以重新运行 |
3 | 声明了抛出InterruptedException异常 | 没有声明抛出任何异常 |
4 | sleep()方法具有比yield()方法更好的移植性,因此建议使用sleep()方法控制并发线程的执行 |
4、改变线程的优先级
每个线程在执行的时候都具有一定的优先级,每个线程的默认优先级与创建它的父线程的优先级相同,在默认情况下,main线程具有普通优先级,由main线程创建的子线程也具有普通优先级。
Thread类提供了setPriority(int newPriority)、getPriority()用来指定和获取指定线程的优先级,数值范围1-10,也有三个静态常量如下:
- Thread.MIN_PRIORITY = 1
- Thread.MAX_PRIORITY = 10
- Thread.NORM_PRIORITY = 5 //默认就是NORM_PRIORITY
注:线程优先级priority,代表概率,不代表决定的先后顺序,只能说会获得更多的执行机会
package com.ly.thread;
/**
* liyang 2020-08-11
*/
public class PriorityTest implements Runnable{
@Override
public void run() {
for(int i = 0; i < 100; i++) {
System.out.println(Thread.currentThread().getName() + ",其优先级是: "
+ Thread.currentThread().getPriority() + ",循环变量是: " + i);
}
}
public static void main(String[] args) {
System.out.println(Thread.currentThread().getPriority());
Thread.currentThread().setPriority(6);
for(int i = 0; i < 30; i++) {
if(i == 10) {
Thread lowThread = new Thread(new PriorityTest());
lowThread.setName("low");
lowThread.start();
System.out.println("创建之初的优先级: " + lowThread.getPriority());
lowThread.setPriority(Thread.MIN_PRIORITY);
}
if(i == 20) {
Thread highThread = new Thread(new PriorityTest());
highThread.setName("high");
highThread.start();
System.out.println("创建之初的优先级: " + highThread.getPriority());
highThread.setPriority(Thread.MAX_PRIORITY);
}
}
}
}
运行结果:
太长,这里描述下,高优先级的会获得较多的执行机会,子线程初始优先级和创建它的线程一致,主线程一开始默认优先级是5,也就是Thread.NORM_PRIORITY = 5。
注:虽然java提供有10个优先级,但是不同的系统对优先级的支持不同,比如Windows 2000仅提供7个优先级,因此应该避免直接使用数值指定线程的优先级,建议采用3个静态常量来设置,以保证好的可移植性。