Thread 类的基本用法
Thread类的概念
Thread 类是 JVM 用来管理线程的一个类,换句话说,每个线程都有一个唯一的 Thread 对象与之关
联。
1.1Thread的常见构造方法
方法 | 说明 |
---|---|
Thread() | 创建线程对象 |
Thread(Runnable target) | 使用Runnable对象创建线程对象 |
Thread(Runnable target,String name) | 使用Runnable对象创建线程对象,并命名 |
Thread(ThreadGroup group,Runnable target) | 线程可以被用来分组管理,分好的组即为线程组 |
Thread t1 = new Thread();
Thread t2 = new Thread(new MyRunnable());
Thread t3 = new Thread("名字");
Thread t4 = new Thread(new MyRunnable(),"名字");
1.2Thread 的几个常见属性
属性 | 获取方法 |
---|---|
ID | getId() |
名称 | getName() |
状态 | getState() |
优先级 | getPriority() |
是否后台线程 | isDaemon() |
是否存活 | isAlive() |
是否被中断 | isInterrupted() |
对于上面几个属性解释
1.ID是线程的唯一的标识,不同的线程不会重复
2.名称是各种调试工具用到
3.状态表示线程当前所处的一个情况
4.优先级高的线程理论上来说更容易被调度到,但实际上并没有什么区别
5.后台线程(守护线程),不会阻止进程结束,后台工作没有做完,进程依旧可以结束,但前台线程会阻止进程结束,前台工作没有做完,进程不能够结束。
6.代码里手动创建的线程,默认都是前台的,包括main默认也是前台的,其他jvm自带的线程都是后台的,但是可以通过手动使用setDaemon()设置为后台线程。
Thread t = new Thread(new Runnable() {
@Override
public void run() {
while (true){
System.out.println("1");
}
}
},"t");
t.setDaemon(true);
t.start();
把t设置为守护线程/后台线程
此时进程的结束与否就和t无关了
7.JVM会在一个进程的所有非后台进程结束后,才会结束运行。
8.是否存活,即run()方法是否结束。
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
while (true) {
System.out.println("hello");
}
}
}, "t1");
t1.start();
while (true) {
try {
Thread.sleep(1000);
System.out.println(t1.isAlive());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
如果t1的run还没跑,isAlive就为false;
如果t1的run正在跑,isAlive就为true;
如果t1的run跑完了,isAlive就为false;
9.线程中断,中断的意思并不是让线程立刻停止,而是通知线程应该要终止,是否真的终止,取决于具体代码。比如,我妈叫我拖地,我正在打游戏,而我现在有三个选择,第一个是马上去;第二个是等打完游戏,再去;第三个是直接摆烂,这里的中断也类似这个意思,根据具体代码进行选择,线程中断的两种方法,在下面进行说明。
1.3启动一个线程-start()
通过run方法创建一个线程对象,但是线程对象被创建出来,并不代表着线程就开始运行,这里就要区别run()和start()的区别:run()方法是描述线程要做的工作;start()方法会让内核创建一个PCB,此时PCB代表一个真正的线程,并让新线程调用run。
即调用start()方法,才真的在操作系统底部创建出一个线程
1.4中断一个线程
目前常见中断线程的两种方法:
1.通过共享的标记来进行沟通
2.调用interrupt()方法来通知
1.使用标志位来控制线程是否要停止
Thread t = new Thread(() -> {
while (true) {
System.out.println("hello");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
t.start();
Thread.sleep(3000);
//在主线程里面可以随时通过flag的变量的取值,来操作t线程是否结束
flag = false;
2.使用Thread自带的标志位,来进行判定
A
Thread t = new Thread(() -> {
while (!Thread.currentThread().isInterrupted()) {
System.out.println("hello");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
t.start();
Thread.sleep(3000);
t.interrupt();
Thread.currentThread().这个是Thread类的静态方法,通过这个方法可以获取当前线程,类似this;
isInterrupted()是在t.run中被调用,这里就是获取t的线程,这里要注意的是true表示被终止,false表示未被终止,所以要在前面取反,才能继续走。
A的代码的结果如下:
为什么会出现这个现象的原因是:当代码执行到t.interrupt(),internet会做两件事情:
1.把线程内部标志位,设置为true
2.如果线程在进行sleep,就会触发异常,把sleep唤醒,但是把sleep唤醒,会导致刚才设置的标志位变为false(即清空了标志位)
所以简单而言,线程t忽略你的请求
如果把代码修改成如下:增加一个break
B
Thread t = new Thread(() -> {
while (!Thread.currentThread().isInterrupted()) {
System.out.println("hello");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
break;
}
}
});
t.start();
Thread.sleep(3000);
t.interrupt();
B的代码的结果如下:
因为break,此时线程t立刻响应你的终止请求
如果把代码修改成如下:增加一个延时
C
Thread t = new Thread(() -> {
while (!Thread.currentThread().isInterrupted()) {
System.out.println("hello");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
try {
Thread.sleep(500);
} catch (InterruptedException ex) {
ex.printStackTrace();
}
break;
}
}
});
t.start();
Thread.sleep(3000);
t.interrupt();
C的代码的结果如下:
虽然运行结果与B一致,但是延迟执行中断
线程t稍后进行终止
1.5等待一个线程
线程是一个随机调度过程,等待线程,就是控制两个线程的结束顺序
下面是示例代码
public static void main(String[] args) {
Thread t = new Thread(() -> {
for (int i = 0; i < 3; i++) {
System.out.println("HELLO");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
t.start();
System.out.println("join之前");
try {
t.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("join之后");
}
代码的结果是
这个代码的实质就是,通过此处的join让当前的main线程来等待t线程执行结束
原本是在执行t.start()之后,t线程和main线程就会并发执行,抢占式执行,main和t都是继续执行下去,但是有了join之后,join会阻塞main线程的执行,一直阻塞到t线程执行结束,main线程才会从join中恢复过来,在继续执行main线程。
假设开始执行join的时候t已经结束,结果又会变成如何?
public static void main(String[] args) {
Thread t = new Thread(() -> {
for (int i = 0; i < 3; i++) {
System.out.println("HELLO");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
t.start();
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("join之前");
try {
t.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("join之后");
}
在join之前添加sleep(5000),使开始执行join的时候t已经结束,结果如下
此时执行join的时候,t已经结束,那么join不会阻塞,会立即执行。
1.6获取当前线程引用
方法 | 说明 |
---|---|
public static Thread currentThread(); | 返回当前线程对象的引用 |
public static void main(String[] args) {
Thread thread = Thread.currentThread();
System.out.println(thread.getName());
}
1.7休眠当前线程
方法 | 说明 |
---|---|
public static void sleep(long millis) throws InterruptedException | 休眠当前线程 millis毫秒 |
public static void sleep(long millis, int nanos) throwsInterruptedException | 可以更高精度的休眠 |
public static void main(String[] args) throws InterruptedException {
System.out.println(System.currentTimeMillis());
Thread.sleep(3 * 1000);
System.out.println(System.currentTimeMillis());
}