Java【多线程】Thread类常见方法

目录

[启动一个线程 start()](#启动一个线程 start())

中断一个线程

方式1.通过共享的标记来进行沟通

方式2.调用interrupt()方法来通知

[等待一个线程 join()](#等待一个线程 join())

休眠当前线程


启动一个线程 start()

java标准库/jvm提供的方法

本质上是调用操作系统的api

这样的方法没有实现,只有一个声明

native 本地方法

jvm是c++来写的

带有native的方法实现是在jvm内部通过c++代码来实现的

(要想看到这部分代码需要额外下载jvm的源码c++)

在c++的实现中内部就会判断当前是哪个操作系统,并且调用对应系统的api来创建线程

每个Thread对象都只能start一次,日抛

每次想创建一个新的线程,都得创建一个新的Thread对象(不能重复利用)

java中期望Thread对象和操作系统中的线程是一一对应

start是系统调用api


中断一个线程

核心:让线程的入口方法能够尽快结束

"终止"此处让线程直接就停止了不会再恢复了

让线程的入口方法执行完毕,线程就随之结束了

(run方法尽快return)

方式1.通过共享的标记来进行沟通

变量捕获

lambda里面希望使用外面的变量

触发"变量捕获"这样的语法

lambda是回调函数

执行时机是很久之后(操作系统真正创建出线程之后才会执行)

很有可能,后续线程创建好了。当前main这里的方法都执行完了,对应的isFinished就销毁了

为了解决上述 的问题

java的做法是,把被捕获的变量给拷贝一份,拷贝给lambda里面

外面的变量是否销毁就不影响lambda里面的执行了

拷贝,意味着,这样的变量就不适合进行修改

修改一方,另一方就不会随之变化的(本质上是两个变量)

这种一边变,一边不变可能给程序员带来更多困惑

java大佬们就想了个办法压根不允许你这里进行修改


把上述代码改成成员变量

此时不再是"变量捕获"语法

而是切换成"内部类访问外部类成员"语法

java 复制代码
public class Demo10 {
    private static boolean isFinished = false;
    public static void main(String[] args) throws InterruptedException {
        Thread t = new Thread(()->{
            while(!isFinished){
                System.out.println("hello Thread");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
            System.out.println("Thread 结束");
        });
        t.start();
        Thread.sleep(3000);
        isFinished = true;
    }
}

lamabda本质上是函数式接口

相当于一个内部类

isFinished变量本身就是外部类 (Demo10)的成员

成员变量生命周期也是让GC来管理的

在lambda里面不担心变量生命周期失效的

也就不必拷贝,也就不必限制final之类的

GC==垃圾回收


方式2.调用interrupt()方法来通知

线程终止

java的Thread对象中提供了现成的变量,直接进行判定,不需要自己创建了

java 复制代码
public class Demo11 {
    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) {

                    break;
                }
            }
            System.out.println("t结束");
        });
       t.start();
       Thread.sleep(3000);
        System.out.println("main线程尝试终止t线程");
       t.interrupt();
    }
}

Thread.currentThread().isInterrupted()判断线程是否被终止了

t.interrupt();主动去进行终止

修改这个boolean变量的值

除了设置boolean变量(标志位)之外

还能够唤醒像sleep这样的阻塞方法

这个操作能够唤醒sleep

sleep会抛出异常

针对异常的处理,使用break结束循环

如果不加上述break(空着的)

针对上述代码

其实是sleep在搞鬼

正常来说,调用Interrupt方法就会修改isInterrupted方法内部的标志位设为true

由于上述代码中,是把sleep给唤醒了

这种提前唤醒的情况下sleep就会在唤醒之后,把isInterrupted标志位设置回false

因此在这样的情况下如果继续执行到循环的条件判定,就会发现能够继续执行

sleep这样设定之后相当于让程序员在catch语句中有更多的选择空间

程序员可以自行决定这个线程是要立即结束还是等会再结束还是不结束

加上break就是立即终止

啥都不写就是不终止

catch中先执行一些其他逻辑再break,就是稍后终止

java中线程的终止不是一个强制性的措施

不是main让t终止t就一定终止,选择权在t自己手上(看t线程自己的代码咋写)

java这里的设定,把决定权交给被终止的线程自己了

线程内部代码中就可以保证,一定是执行出一些"阶段性"的结果,然后才真正退出


上述这里抛出异常的方法不只是sleep会有

等待一个线程 join()

多个线程之间 并发执行 随机调度

join能够要求,多个线程之间,结束的先后顺序

比如。再主线程中调用t.join()

就是让主线程等待t线程先结束

java 复制代码
public class Demo12 {
    public static void main(String[] args) throws InterruptedException {
        Thread t =new Thread(()->{
            for (int i = 0; i < 3; i++) {
                System.out.println("hello Thread");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
            System.out.println("t线程结束");
        });
        t.start();

        t.join();
        System.out.println("main线程结束");
    }
}

在main线程中调用t.join

效果:让main线程等待t先结束

当执行到t.jion此时main线程就会"阻塞等待"

一直等到t线程执行完毕,join才能继续执行

不见不散,只要t不结束main的join就会一直等下去

join提供了带参数的版本,指定"超时时间"等待的最大时间

带有超时时间的等待才是更科学的做法

哪个线程调用这个方法返回哪个线程的引用(类似于this)


休眠当前线程

因为线程的调度是不可控的所以这个方法只能保证实际休眠时间是大于等于参数设置的休眠时间

sleep

写了sleep1000很可能比1000略多一点

代码调用sleep相当于让当前线程让出cpu的资源

后续时间到了的时候需要操作系统内核把这个线程重新调到cpu上才能继续执行

时间到意味着允许被调度了

而不是立即就执行了

sleep(0)使用sleep的特殊写法

写了sleep(0)意味着让当前的线程放弃cpu资源(把cpu让出来给别人更多的执行机会,等待操作系统重新调度

相关推荐
yngsqq几秒前
c#使用高版本8.0步骤
java·前端·c#
流星白龙3 分钟前
【C++习题】10.反转字符串中的单词 lll
开发语言·c++
尘浮生10 分钟前
Java项目实战II基于微信小程序的校运会管理系统(开发文档+数据库+源码)
java·开发语言·数据库·微信小程序·小程序·maven·intellij-idea
MessiGo11 分钟前
Python 爬虫 (1)基础 | 基础操作
开发语言·python
小白不太白95014 分钟前
设计模式之 模板方法模式
java·设计模式·模板方法模式
Tech Synapse16 分钟前
Java根据前端返回的字段名进行查询数据的方法
java·开发语言·后端
xoxo-Rachel23 分钟前
(超级详细!!!)解决“com.mysql.jdbc.Driver is deprecated”警告:详解与优化
java·数据库·mysql
乌啼霜满天24925 分钟前
JDBC编程---Java
java·开发语言·sql
色空大师37 分钟前
23种设计模式
java·开发语言·设计模式