JavaEE|多线程(二)

线程的命名

可以给线程起名(中文名也是可以的),例如对三个线程起不同名字再去jconsole上观察

java 复制代码
public class Demo06 {
    public static void main(String[] args) {
        Thread t1=new Thread(()->{
            while(true){
                System.out.println("hello t1");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        });
        Thread t2=new Thread(()->{
            while(true){
                System.out.println("hello t2");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        });
        Thread t3=new Thread(()->{
            while(true){
                System.out.println("hello t3");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        });
    

当不给线程起名的时候,线程会自动命名为Thread-0,Thread-1....

通过名字描述线程是干啥的,方便调试

主线程

main方法执行完毕,主线程就已经结束了

以前我们对于main的认识是当main方法执行完之后,程序就已经结束了(进程);但实际上,以前的

认知是针对于单线程程序

Thread 类及常见方法

Thread 类是JVM用来管理线程的一个类,换句话说,每个线程都有一个唯一的Thread 对象与之关 联。

Thread 的常见构造方法

|-----------------------------------------------|----------------------------|
| 方法 | 声明 |
| Thread() | 创建线程对象 |
| Thread(Runnable target) | 使用Runnable对象创建线程对象 |
| Thread(String name) | 创建线程对象,并且命名 |
| Thread(Runnable target,String name) | 使用Runnable对象创建线程对象,并命名 |
| 【了解】Thread(ThreadGroup group,Runnable target) | 线程可以被用来分组管理,分好的组即为线程组,了解即可 |

Thread 的几个常见属性

|--------|-----------------|
| 属性 | 获取方法 |
| ID | getId() |
| 名称 | getName() |
| 状态 | getState() |
| 优先级 | getPriority() |
| 是否后台线程 | isDaemon() |
| 是否存活 | isAlive() |
| 是否被中断 | isInterrupted() |

ID 是线程的D唯一标识,不同线程不会重复,标识线程身份的效果,类似于PID

Daemon

关于后台线程,需要记住一点:JVM会在一个进程的所有非后台线程结束后,才会结束运行。

例如,当main线程虽然结束了,但是t1,t2.t3还在,所以进程仍然存在,t1,t2,t3这样的线程就称为

"前台线程"

黑框部分是JVM自带的线程,这些线程不影响进程的结束,如果进程要结束了,即使这些线程继续

运行,也会随之结束,这些就是后台线程

我们创建的代码包括main线程都默认是前台线程,但可以通过setDaemon方法来进行修改

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

把t设为后台线程,无力阻止main线程的结束,这样的设置得在start之前运行

isAlive()

创建Thread对象和系统中的线程关系是一一对应的,但是Thread对象的生命周期和系统中的线程的生命周期是不同的(可能存在,Thread对象存活,但是系统中的线程已经销毁的情况)

java 复制代码
  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.isAlive());
        t.start();
        while (true){
            System.out.println(t.isAlive());
            Thread.sleep(1000);
        }

当for循环结束之后,线程的入口方法的逻辑结束了,系统中对应的线程就随之销毁了

但是对应的t对象会仍然存在

启动线程-start()

java标注库/JVM提供的方法,本质上是调用操作系统的API

每个Thread对象,都是只能start一次的

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

start和run的区别

run是线程的入口方法,不需要手动调用

start是调用系统api

二者本质上没有很大的关系

中断线程

1.通过共享的标记来终止

java 复制代码
  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);
                }
            }
        });
        t.start();
        Thread.sleep(3000);
        isFinished=true;
    }

通过定应全程变量来控制线程进度

但当isFinished变为局部变量时,此时无法对其进行修改

通过观察报错原因我们可以知道,这个isFinished应该为常量

这与我们前面的isFinished修改相互矛盾,这其实是因为lambda里面希望使用外面的函数,触发了

变量捕获的语法

lambda是回调函数,执行时机是很久之后(操作系统真正创建线程之后,才会执行),可能会出

现后续线程创建好了,但是当前main这里的方法都执行完了,对应的isFinished就销毁了

Java为解决上述的问题把捕获的变量拷贝一份在lambda里面,外面的变量是否销毁都不会影响

lambda里面的执行

这里的拷贝意味着这样的变量就不再适合修改(修改的本质是修改一方,另一方不会随之变化)

让isFinished变为静态变量时,isFinished在类加载时就会存在,而不是通过new时才会存在

而对于引用类型,引用本身是不能修改(不能修改这个引用指向的其他对象),但是引用指向的对象

本体是可以修改的

把上述的代码改成员变量此时不再是"变量捕获"语法,而是切换成"内部类访问外部类的成员"语法

lambda本质上是函数式接口,相当于一个内部类,isFinished就是外部类的成员,内部类就是能够

访问到外部类的成员,成员变量生命周期也是让GC来管理的,在lambda里面不担心变量生命周期

失效的问题,也就不必拷贝,也就不必限制final之类的

调用 interrupt() 方法

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

lambda这里的定义是在new Thread之前,也是在Thread t声明之前,所以Thread创建的对象也无

法在lambda表达式中使用

Thread提供了访问当前对象的静态方法currentThread(),获取到的就是那个线程的Thread引用

java 复制代码
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) {
                    throw new RuntimeException(e);
                }
            }
            System.out.println("t结束了");
        });
        t.start();
        Thread.sleep(3000);
        System.out.println("main尝试终止t线程");
        t.interrupt();
        System.out.println(Thread.currentThread().getName());
    }

判定Thread里的boolean变量的值,判定线程是否终止了

主动去进行终止,修改这个boolean变量的值,除了设置boolean变量(标志位)之外,还能够唤醒

sleep这样的阻塞方法

这个线程掀桌了,可以通过修改抛出异常的部分来控制终止

加上break是立即终止

啥都不写就是不终止

catch中执行一些逻辑在break,就是稍后终止

相关推荐
_Evan_Yao1 小时前
RAG中的“Chunk”艺术:我试过10种切分策略后总结的结论
java·人工智能·后端·python·软件工程
烤麻辣烫2 小时前
JS基础
开发语言·前端·javascript·学习
froginwe112 小时前
C++ 文件和流
开发语言
魂梦翩跹如雨2 小时前
数据库的“契约” —— 约束(Constrains)
java·数据库·mysql
Dxy12393102162 小时前
Python在图片上画矩形:从简单边框到复杂标注的全攻略
开发语言·python
独自破碎E2 小时前
面试官:你有用过Java的流式吗?比如说一个列表.stream这种,然后以流式去处理数据。
java·开发语言
꯭爿꯭巎꯭2 小时前
python下载手机版(python3手机版(免费))
开发语言·python·智能手机
网域小星球3 小时前
C++ 从 0 入门(六)|C++ 面试必知:运算符重载、异常处理、动态内存进阶(终极补充)
开发语言·c++·面试
2601_949818093 小时前
头歌答案--爬虫实战
java·前端·爬虫