目录
Thread 类是 JVM 用来管理线程的一个类 , 换句话说 , 每个线程都有唯一的 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) | 线程可以被用来分组管理 , 分好的组即为线程组 , 目前了解即可. |
给线程创建的对象命名是为了方便在各种调试工具中调试.
class MyRunnable implements Runnable{
@Override
public void run() {
System.out.println("Hello Thread");
}
}
public class ThreadDemo2 {
public static void main(String[] args) {
Thread t1 = new Thread();
Thread t2 = new Thread("线程的名字");
Thread t3 = new Thread(new MyRunnable());
Thread t4 = new Thread(new MyRunnable() , "线程的名字");
}
}
2.Thread 的几个常见属性
属性 | 获取方法 |
ID | getId() |
名称 | getName() |
状态 | getState() |
优先级 | getPriority() |
是否后台线程 | isDaemon() |
是否存活 | isAlive() |
是否被中断 | isInterrupted() |
- ID 是线程的唯一标识 , 不同线程不会重复.
- 名称 在各种调试工具中用到.
- 状态表示当前线程所处的一个情况.
- 优先级高的线程 , 理论上来讲更容易被调度到.
- 关于后台线程需要注意 , 后台线程的结束与否不会影响到进程.
- 是否存活 , 通俗来讲就是 run() 是否结束了.
- 关于是否中断 , 下面专门会讲.
3. 启动一个线程
之前我们以及知道 , 如何通过覆盖 run() 方法覆盖一个线程对象 , 但线程对象被创建出来并不意味着线程就开始运行了 .
- 覆盖 run() 方法是提供给线程要做的事情的指令清单.
- 创建线程对象可以认为是把张三 , 李四叫到一起.
- 而调用 start() 方法才是喊了一声行动 , 线程才真正的执行起来.此时操作系统才会在底层创建一个线程.
4. 中断一个线程
李四一旦进入工作状态 , 就会按照行动指南一直执行直到结束. 但有时出现突发状况 , 例如老板临时改变主意或者发现汇款对象是个骗子 , 这时就需要停止转账. 那么张三应该如何通知李四呢?这就涉及到中断线程的操作.
目前常见的有以下两种方式:
- 1.通过共享标记来进行沟通.
- 2.调用 Interrupt() 方法来通知.
示例一: 通过共享标记为来沟通.
在主线程中就可以随时通过 flag 变量的取值 , 来操作 t 线程是否结束 , 但这种方式有一个明显的缺点就是不能及时响应 , 例如while循环中的休眠时间较长就需要一直等待.
public class ThreadDemo2 {
public static boolean flag = true;
public static void main(String[] args) {
Thread t = new Thread(()->{
while (true){
System.out.println("Hello thread");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
});
t.start();
Thread.sleep(3000);
//此时在主线程中就可以随时通过 flag 变量的取值 , 来操作 t 线程是否结束.
flag = false;
}
}
示例二: 调用 Interrupt() 方法来通知
currentThread() 是Thread类的静态方法 , 通过这个方法可以获取到当前线程 , 哪个线程调用这个方法哪个线程 , 就会得到哪个线程对象的引用.类似于 this. isInterrupted() 相当于上面例子中的标志位 , 为 true 表示终止 , 为 fasle 表示未被终止. t.interrupt() 就是终止线程.
Tips : 如果线程在 sleep 中休眠 , 此时调用 interrupt 就会出发 sleep 内部的异常(InterruptedException) , 导致sleep提前返回.
public static void main(String[] args) throws InterruptedException {
Thread t = new Thread(()->{
while (!Thread.currentThread().isInterrupted()){
System.out.println("Hello thread");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();;
}
}
});
t.start();
Thread.sleep(3000);
t.interrupt();
}
此时运行代码就会发现问题 , 明明抛出异常但进程还在执行.
这是因为 interrupt 虽然会做两件事:
- 把线程内部的标志位 boolean 给设置成 true.
- 如果线程在进行 sleep , 就会触发异常并且把 sleep 唤醒.
但 sleep 被唤醒后 , 还会做一件事 , 把刚才设置的这个标志位 , 再设置成 false .(清空标记为) 可以形象的理解成 sleep 有"起床气". 为了解决这一问题 , 可以在catch语句后加 break .线程立即响应你的请求. 为什么唤醒 sleep 后要清空标志位呢? 这是为了把唤醒 sleep 后 , 程序是否要终止的选择权交给程序员自己.
5. 等待一个线程
由于线程的执行是一个随机调度的过程 , 等待线程要做事情就是更好的控制线程的执行顺序.
方法 | 说明 |
public void join(); | 等待线程结束 |
public void join(long millis); | 等待线程结束 , 最多等millis毫秒 |
public void join(long millis , int nanos); | 同理 , 精度更高. |
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上大数据知识点,真正体系化!
多等millis毫秒 |
| public void join(long millis , int nanos); | 同理 , 精度更高. |
[外链图片转存中…(img-YKejdTdA-4701991875989)]
[外链图片转存中…(img-XSRSKmsm-4701991875989)]
[外链图片转存中…(img-FLHn2Hdg-4701991875989)]
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上大数据知识点,真正体系化!