多线程基础保姆级教程

多线程基础

中断一个线程

通常使用Thread.currentThread().isInterrupted() t.isInterrupted()配合使用来中断一个线程

currentThread()方法用来获取当前线程的是咧,哪个线程调用就返回哪个线程的对象。

Thread内部有一个标志位,这个标志位就可以用来判断线程是否结束
上篇讲到了,手动设置标志位,当线程内部在sleep的时候,主线程修改变量,新线程不能及时响应

而t.interrupt()就可以把Thread内部的这个标志位设置位true,及时线程内部的逻辑出现阻塞(sleep),也是可以用这个方法唤醒的。

正常来说,sleep会休眠到时间到,才能唤醒。此处给出的interrupt就可以使sleep内部触发一个异常,从而提前被唤醒。而我们自己设置的标志位无法实现这个效果。
例子

java 复制代码
在这里插入代码package thread;

public class Demo001 {
    public static void main(String[] args) {
        Thread t = new Thread(()->{
            //Thread头部有一个现成的标志位,可以用来判定当前循环是否结束
            while(!Thread.currentThread().isInterrupted()){
                System.out.println("线程工作中");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }

        });
        t.start();

        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        System.out.println("让t终止");
        t.isInterrupted();
    }
}

运行结果

运行结果显示,异常确实是出现了,sleep确实唤被醒了,但是上述t仍然在继续工作!!并没真正结束!!!

interrupt()虽然唤醒线程了,此时sleep()方法会抛出异常,同时会自动清除刚才设置的标志位。这样就使得 设置标志位 这样的效果好像没生效一样。

为啥这么设定??

java这样设定是期望,当线程收到 要中断 这样的信号时候,它能够自由决定,接下来该怎么处理?

小例子:

有一天,我正在打游戏,我吗让我下楼买瓶酱油

当我收到这个信号,就有三种做法

1.直接丢下游戏,去买酱油

2.我把这局打完再去

3.直接忽略,假装没听见

同样的线程也可以采取这三种方式来执行

这样的目的是为了让线程有更多的 可操作空间。而这种可操作空间的前提是通过异常(异常可以清楚设置好的标志位,从而给了线程更多可操作的空间)。如果没有sleep(),没有抛出异常,就没有上述的可操作空间。

线程等待

join()方法是让一个线程等待另一个线程执行结束再继续执行,一般来说,等待操作都是要有一个超时时间的。本质上就是控制线程的执行顺序

t.join()的工作过程
(1)如果t线程正在运行,此时调用join的线程就会阻塞,一直阻塞到t线程执行结束为止
(2)如果t1线程已经执行结束了,此时调用join线程,就直接返回了,不会涉及到阻塞

public void join(long millis) 最多等 millis 毫秒,

public void join(long millis, int nanos) 同理,但可以更高精度

实际开发中一般不建议死等,最好要带有"超时时间"

获取当前线程引用

例子

java 复制代码
public class ThreadDemo {
    public static void main(String[] args) {
        Thread thread = Thread.currentThread();
        System.out.println(thread.getName());
   }
}

休眠当前线程

注意:因为线程的调度是不可控的,所以,这个方法只能保证实际休眠时间是大于等于参数设置的休眠时间的。 例如:sleep(1000),

系统会按照1000这个时间来控制让线程休眠 但是当1000时间到了之后,系统会唤醒这个线程.(阻塞->就绪)

但是不是说这个线程成了就绪状态,就能立即回到cpu上运行??(这中间有一个"调度"开销)

对于windows或者linux这样的系统来说,调度开销很大,可能达到ms级别.

有些场景,可能对于时间精度要求是很高的比如,发射卫星;或者导弹拦截 往往需要使用"实时操作系统",任务调度的开销在一定时间范围之内.

为了实时,也有很多的限制,功能上是不如windows和linux的.

观察线程的所有状态

线程的状态是一个枚举类型 Thread.State

java 复制代码
public class ThreadState {
    public static void main(String[] args) {
        for (Thread.State state : Thread.State.values()) {
            System.out.println(state);
       }
   }
}

线程状态和状态转移的意义

大家不要被这个状态转移图吓到,我们重点是要理解状态的意义以及各个状态的具体意思。

NEW:Thread对象已经有了.start方法还没调用

TERMINATED:Thread对象还在,内核中的线程已经没了.

RUNNABLE:就绪状态(线程已经在cpu上执行了/线程正在排队等待上cpu执行)

TIMED_WAITING:阻塞.由于sleep这种固定时间的方式产生的阻塞

WAITING:阻塞.由于wait这种不固定时间的方式产生的阻塞

BLOCKED:阻塞.由于锁竞争导致的阻塞

注意:

BLOCKED 表示等待获取锁, WAITING 和 TIMED_WAITING 表示等待其他线程发来通知.

TIMED_WAITING 线程在等待唤醒,但设置了时限; WAITING 线程在无限等待唤醒

相关推荐
大数据编程之光10 分钟前
Flink Standalone集群模式安装部署全攻略
java·大数据·开发语言·面试·flink
爪哇学长24 分钟前
双指针算法详解:原理、应用场景及代码示例
java·数据结构·算法
ExiFengs28 分钟前
实际项目Java1.8流处理, Optional常见用法
java·开发语言·spring
paj12345678929 分钟前
JDK1.8新增特性
java·开发语言
繁依Fanyi40 分钟前
简易安卓句分器实现
java·服务器·开发语言·算法·eclipse
慧都小妮子1 小时前
Spire.PDF for .NET【页面设置】演示:打开 PDF 时自动显示书签或缩略图
java·pdf·.net
m51271 小时前
LinuxC语言
java·服务器·前端
IU宝1 小时前
C/C++内存管理
java·c语言·c++
瓜牛_gn1 小时前
依赖注入注解
java·后端·spring
hakesashou1 小时前
Python中常用的函数介绍
java·网络·python