目录
Thread类及常见方法
Thread类是JVM用来管理线程的一个类,换句话说,每个线程都有唯一的Thread对象与之关联。
1.Thread的常见构造方法
方法 | 说明 |
Thread | 创建线程对象 |
Thread(Runnable target) | 使用Runnable对象创建线程对象 |
Thread(String name) | 创建线程对象,并命名 |
Thread(Runnable target,String name) | 使用Runnable对象创建线程对象并命名 |
Thread(ThreadGroup group,Runnable target) | 线程可以被用来分组管理,分好的组即为线程组,这个目前了解即可。 |
这里第二和第三行的参数多了name,目的是为了方便测试。
public class ThreadD6 {
public static void main(String[] args) {
Thread t = new Thread(new Runnable() {
@Override
public void run() {
while(true){
System.out.println("ThreadD6");
}
}
},"D6");
t.start();
}
}
2.Thread的几个常见属性
属性 | 获取方法 | 备注 |
ID | getId() | |
名称 | getName() | 构造方法里起的名字 |
状态 | getState() | 线程状态(Java里面的线程状态比操作系统原生状态更丰富) |
优先级 | getPriority() | 可获取,可设置,但是没有用 |
是否是后台线程(守护线程) | isDaemon() | (1)前台线程会阻止进程结束,前台线程的工作没做完,进程是完成不了的,后台线程不会阻止线程的结束。 (2)代码中手动创建的线程一般都是前台线程,包括在main()函数也默认是前台的,其他的jvm自带的线程都是后台的。 (3)也可以使用setDaemon()来设置后台线程。 |
是否存活 | isAlive() | (1)判断当前系统里的这个线程是不是真的有了。如果内核里的线程把run跑完了,此时线程销毁,pcb随之释放,但是Thread t 这个对象不一定被释放,此时的isAlive也是false。 (2)t.start:让内核创建一个pcb,此时pcb表示一个真正的线程。 (3)在真正调用start之前,调用t.Alive()就是false。调用strat之后,isAlive就是true. |
是否被中断(终止) | isInterrupted() | 不是让线程立即终止,而是通知线程应该要停止了,是否真的停止取决于线程这里具体的写法, (1)使用标志位来控制线程是否要停止。(自定义变量这种方式不能及时响应,尤其是在sleep进行休眠的时候) (2)使用Thread自带的标志位进行预判。(唤醒上面sleep这样的方法) |
(1)setDaemon()
代码:
public class ThreadD6 {
public static void main(String[] args) {
Thread t = new Thread(new Runnable() {
@Override
public void run() {
while(true){
System.out.println("helloD6");
}
}
},"D6");
t.setDaemon(true);
t.start();
}
}
结果:
不设置成后台线程的输出结果:
把t设置成守护线程/后台线程,此时进程的结束与否就和t无关了。
(2)isAlive()
(1)内核里的线程把run跑完了,此时线程销毁,pcb随之释放,但是Thread t 这个对象不一定被释放,此时的isAlive也是false。
public class ThreadD6 {
public static void main(String[] args) {
Thread t = new Thread(new Runnable() {
@Override
public void run() {
System.out.println("hello");
}
},"D6");
t.start();
while(true){
try {
Thread.sleep(1000);
System.out.println(t.isAlive());
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
}
结果:
使用一个for循环, 使run()先休眠三秒再继续往下执行,这里的isAlive()首先打印出来的就是false;
public class ThreadD6 {
public static void main(String[] args) {
Thread t = new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 3; i++) {
System.out.println("D6");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
},"D6");
t.start();
while(true){
try {
Thread.sleep(1000);
System.out.println(t.isAlive());
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
}
执行结果:
如果t的run还没跑,isLive就是false
如果t的run正在跑,isLive就是true
如果t的run跑完了,isLive就是false
3.抢占式执行
在进程执行的过程中,哪一部分先执行,哪一部分后执行是随机的。
例如:
public class ThreadD7 {
public static void main(String[] args) {
Thread t = new Thread(() ->{
System.out.println("D7");
});
t.start();
System.out.println("mainD7");
}
}
在上面的代码中,主函数有一个线程,还有一个调用的线程,这里的
这两部分的代码谁先执行谁后执行是不能被确定的,是随机的执行。
4.中断一个线程
不是让线程立即终止,而是通知线程应该要停止了,是否真的停止取决于线程这里具体的写法.
(1)使用标志位来控制线程是否要停止。(自定义变量这种方式不能及时响应,尤其是在sleep进行休眠的时候)
public class ThreadD8 {
private static boolean flag = true;
public static void main(String[] args) {
Thread t = new Thread(() ->{
while (flag){
System.out.println("D8");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
});
t.start();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
flag = false;
}
}
结果:
(2)使用Thread自带的标志位进行预判。(唤醒上面sleep这样的方法)
public class ThreadD9 {
public static void main(String[] args) throws InterruptedException {
Thread t = new Thread(() ->{
while (!Thread.currentThread().isInterrupted()){
System.out.println("D9");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
});
t.start();
Thread.sleep(1000);
t.interrupt();
}
}
结果:
interrupt会做两件事:
(1)把线程内部的标志位(boolean)给设置成true
(2)如果线程在进行sleep,就会触发异常,就会把sleep唤醒
但是!!在sleep在唤醒的时候,还会做一件事,把刚才设置的这个标志位,再设置成false(清空了标志位)。
需要注意的是,调用interrupt,只是通知终止,不是线程一定要乖乖终止。
终止分为以下三种:
(1)忽略了请求,继续执行
运行结果:
完整代码:
public class ThreadD9_1 {
public static void main(String[] args) throws InterruptedException {
Thread t = new Thread(() ->{
while (!Thread.currentThread().isInterrupted()){
System.out.println("D9");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
t.start();
Thread.sleep(3000);
t.interrupt();
}
}
(2)线程t立即响应请求
运行结果:
完整代码:
public class ThreadD9_2 {
public static void main(String[] args) throws InterruptedException {
Thread t = new Thread(() ->{
while (!Thread.currentThread().isInterrupted()){
System.out.println("D9");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
break;
}
}
});
t.start();
Thread.sleep(3000);
t.interrupt();
}
}
(3)稍后进行终止
结果;
完整代码:
public class ThreadD9_3 {
public static void main(String[] args) throws InterruptedException {
Thread t = new Thread(() ->{
while (!Thread.currentThread().isInterrupted()){
System.out.println("D9");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
try {
Thread.sleep(5000);
} catch (InterruptedException ex) {
ex.printStackTrace();
}
break;
}
}
});
t.start();
Thread.sleep(3000);
t.interrupt();
}
}
5.等待一个线程join()
等待一个线程结束。等待线程:控制执行顺序。
有时我们需要等待一个线程完成他的工作后,才能进行自己的下一步工作。
方法 | 说明 | 解释 |
public void join() | 等待线程结束 | ”死等“,无参数版 |
public void join (long millis) | 等待线程结束,最多等待millis毫秒 | 指定一个超时时间(最大等待时间) |
piblic void join(long millis,int nanos) | 同理,但是可更高精度 |
代码:
public class ThreadD10 {
public static void main(String[] args) {
Thread t = new Thread(() -> {
for (int i = 0; i < 3; i++) {
System.out.println("D10");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
});
t.start();
System.out.println("join之前");
try {
t.join();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println("join之后");
}
}
结果: