首页 > 编程语言 >Javaee---多线程(一)

Javaee---多线程(一)

时间:2024-10-21 18:46:19浏览次数:9  
标签:main 这个 Thread Javaee --- start 线程 多线程 我们

在这里插入图片描述

文章目录

1.线程的概念

(下面的这个图片里面的代码有错误,我们new的这个mythread类的对象,应该是使用这个Thread这个类进行接收,这个是个向上转型的过程);

  • 我们的这个里面使用到了这个thread类,但是不需要进行这个import的操作,这个主要就是因为我们的这个Thread是位于这个java.lang这个包下面的;像我们之前使用的这个String也是在这个包下面,我们也没有进行这个import的操作;
  • 这个Thread里面是有这个run方法的,我们的这个mythread类里面对于这个夫类里面的方法进行了重写;
  • 我们的main方法里面执行这个start时候,就会进入这个run入口进而对于这个内容进行打印输出;

image-20241019192827539

实际上打印输出的时候main方法里面的内容和我们的自定义类里面的这个内容是独立进行的,两个并不会相互依赖;每一个线程都是一个独立的执行流;

image-20241019211712898

当我们把这个start修改为这个run之后,这个就会先去执行我们自己的自定义类里面的这个重写的run方法,这个执行完成之后才会继续执行这个下面的helli main打印输出,但是因为我们的这个是死循环,因此这个会一直打印输出我们的hello thread;

image-20241019211737878

2.休眠里面的异常处理

为了更加清楚的看到这个打印的效果,我们可以对于这个程序进行休眠操作,就是使用这个sleep方法,但是这个方法在进行使用的时候可能会出现一些问题,就是异常情况,针对于自定义类和main方法里面的异常,我们的处理手段是不一样的:

下面的这个异常是受查异常,需要进行显示处理:要么throws,要么try-catch

重写父类的方法,父类的方法里面没有throws,因此我们不可以throws;

image-20241020212336753

image-20241020212513823

相比之下,在这个main里面的这个内容都是我们自己写的,因此这个时候我们就可以使用这个throws方法对于这个出现的异常进行抛出;

image-20241020213545037

有了上面的两个休眠的操作,这个时候我们重新运行这个程序,这个时候就会发现这个两个语句的打印输出的速度就变慢了,但是这个出现的先后顺序其实并不是确定的,这个主要取决于我们的操作系统对于这个线程的调度(调度器)的具体实现,因此我们可以看到有的时候是这个hello thread先打印输出,但是有的时候是这个hello main先打印输出;

3.实现runnable接口

下面的这个就是实现我们的这个runnable接口的方式,两个类里面的这个异常的处理方式还是一样的,一个是捕捉异常,一个就是抛出异常;

但是在这个主方法里面,我们使用这个已有的MyRunnable类实例化对象,相当于是定义了一个任务,然后我们吧这个传递到我们的这个new Thread这个参数里面去,相当于就是使用这个Thread去执行这个任务,其他的和上面的这个方式是没有任何区别的;

针对于这个方式,实现了这个runnable接口的这个类的实例化对象作为我们的这个Thread接口的构造方法的参数,这个和上面最大的不同就是解耦合:耦合指的就是我们的一个程序里面的不同模块之间的影响的程序,解耦合就是解除不同的模块之间的相互的影响;

这样的解耦合的好处就是我们只是定义了这个任务,但是交给谁去执行这个任务就没那么重要了,这个就是这个实现接口的方式和上面的最大不同;
在这里插入图片描述

4.匿名内部类子类创建线程

public class Test {
    public static void main(String[] args) throws InterruptedException {
        //匿名内部类,不知道这个类叫什么,但是知道这个类是我们的Thread的子类
        //不知道这个类的名字也不重要,因为这个类我们只会使用一次
        
        //下面的这个new后面的这个其实就可以理解为是一个类,使用这个没有名字的类实例化对象,在这个匿名的类里面对于这个方法进行重写,里面的这个对于异常的处理和定义两个类的时候是一样的;
        Thread t = new Thread(){
            @Override
            public void run() {
                while(true){
                    System.out.println("hello thread!");
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                }
            }
        };
        
        //使用我们创建的这个线程去开始这个线程的执行,start就是这个线程执行的入口
        t.start();
        while(true){
            System.out.println("hello main!");
            Thread.sleep(1000);
        }

    }
}

5.匿名内部类接口创建线程

public class Test {
    public static void main(String[] args) throws InterruptedException {
        //匿名内部类,不知道这个类叫什么,但是知道这个类是我们的Thread的子类
        //不知道这个类的名字也不重要,因为这个类我们只会使用一次
        //下面的这个就是我们不知道这个类叫什么名字,但是这个类实现了这个接口Runnable
        //因此在这个匿名内部类里面对于这个接口里面的方法进行了重写
        Runnable runnable = new Runnable() {
            @Override
            public void run() {
                while(true){
                    System.out.println("hello thread!");
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                }
            }
        };

        //这个就是我们的这个实现接口的类的实例化对象作为一个参数进行传递
        Thread t=new Thread(runnable);
        t.start();
        while(true){
            System.out.println("hello main!");
            Thread.sleep(1000);
        }

    }
}

6.基于lambda表达式进行线程创建

lambda实际上是这个匿名内部类的一个简化的方式:本质上就是一个函数表达式

public class test {
    public static void main(String[] args) throws InterruptedException {
        //lambda表达式是一个匿名函数,主要是实现回调函数的效果
        //回调函数是计算机里面的一个很重要的术语
        //回调函数--函数指针:函数指针实现转移表,减少if-else分支数目
        //使用函数指针作为回调函数--实现qsort()的比较
        
        //其实这个lambda表达式并不是很复杂,形式上就是这个()->{},我们的这个相关的代码就是写在这个大括号			里面的
        Thread t = new Thread(()->{
            while(true){
                System.out.println("hello thread!");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        });

        t.start();
        while(true){
            System.out.println("hello main!");
            Thread.sleep(1000);
        }

    }
}

7.关于Thread的其他的使用方法

上面我们介绍两个:

1.直接调用无参数的,就是我们最开始介绍的;

2.传递的参数是我们的实现接口的类的实例化对象;

除此之外,我们还可以传递name参数,给这个线程起名字,对于线程进行区分:

7.1线程的名字

public class Test {
    public static void main(String[] args) {
        //String name;
        Thread t = new Thread(() -> {
            while(true){
                System.out.println("hello thread!");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"这个是新的线程" );
        //上面的这个就是相当于是在原来的这个表达式的后面多了这个name属性,这样我们使用这个jconsole工具			进行查看的时候,我们就可以看到自己对于这个线程的命名了;

        t.start();
    }
}

7.2设置为前台线程

还是上面的这个程序,其实在这个默认的情况下,这个线程就是前台线程,就是我们的这个线程不结束,这个程序就不会结束,但是我们的后台线程就是这个线程结束与否,对于我们的整个程序的结束没有影响;

这个默认情况下是前台线程,我们可以使用这个setDaemon方法把这个线程设置为后台的线程;

我们的这个具体的差别可以在打印的时候看出来,就是我们的这个内容好像没有进行打印,这个程序就结束了;

public class Test {
    public static void main(String[] args) {
        //String name;
        Thread t = new Thread(() -> {
            while(true){
                System.out.println("hello thread!");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"这个是新的线程" );

        //设置为后台的线程,运行起来就不会进行打印
        t.setDaemon(true);

        t.start();
    }
}

7.3判断线程是否存活

这个就是使用的t.isAlive方法判断这个线程是不是存活的,下面的这个使用start开始的时候,就是存活的线程,打印输出的结果就是true,但是休眠之后,这个就是死亡的,打印输出false;

public class Test {
    public static void main(String[] args) throws InterruptedException {
        Thread t = new Thread(()->{
            System.out.println("线程开始");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("线程结束");
        });
        t.start();
        System.out.println(t.isAlive());
        Thread.sleep(2000);
        System.out.println(t.isAlive());
    }
}

8.创建线程方法总结

image-20241021125053812

9.start方法

start和run方法的区别:

我们的start方法会去调用这个系统里面的api,进行线程的创建;

run只会去执行这个线程里面的内容(会在这个start创建线程之后自动被调用);

两个的本质区别就是是不是调用了这个系统里面的api,创建出来了新的线程;

10.终止(中断)线程-interrupt

10.1自定义标志位

需要注意的就是这个自定义的静态的变量需要是全局的,不可以是局部的,因为这个lambda是变量的捕捉,这个变量需要是这个final或者是视为这个final的变量;

我们的这个变量一旦写作这个局部的,这个时候我们下面不可以进行修改了,但是我们的有修改,所以写成局部的就会报错,大家可以自己去尝试一下,看看这个效果,这个主要是lambda的语法导致的;

image-20241021132639605

10.2使用系统里面的标志位

为什么会有下面的这个,因为上面的存在缺陷,就是我们需要手动创建,而且我们的线程如果正在休眠,我们把这个标志位修改掉,这个线程无法及时的做出回应,或者是叫做响应;

但是如果我们使用这个系统里面的标志位,这个就是currentThread().isInterrupted()方法进行判断当前的这个线程是不是被打断了;

我们的这个里面有一个t.sleep()这个方法就是进行线程的唤醒,上面的这个手动创建标志位的方法,如果我们的这个线程在休眠,这个时候不可以及时的进行响应,但是我们的这个interrupt这个方法,就会触发这个线程的异常,让这个休眠的线程提前被唤醒,但是我们在运行的时候会发现这个会抛出异常之后,还是会继续执行的;

这个主要原因就是我们的interrupt唤醒这个线程之后,我们的异常抛出把这个设置的标志位清除掉了,就是我们的自动设置的标志位没有了效果,我们的异常让这个标志位的效果失效了,接下来,线程可以自己决定接下来如何进行处理;

image-20241021174301710

11.线程等待join

下面的这个程序就是我们的t线程会休眠,我们的t.join就会让这个主线程等待我们的t线程,直到我们的t线程执行结束,主线程才会结束;

image-20241021181517994

12.线程方法总结

image-20241021183414460
动设置的标志位没有了效果,我们的异常让这个标志位的效果失效了,接下来,线程可以自己决定接下来如何进行处理;

在这里插入图片描述

11.线程等待join

下面的这个程序就是我们的t线程会休眠,我们的t.join就会让这个主线程等待我们的t线程,直到我们的t线程执行结束,主线程才会结束;

在这里插入图片描述

12.线程方法总结

在这里插入图片描述

标签:main,这个,Thread,Javaee,---,start,线程,多线程,我们
From: https://blog.csdn.net/binhyun/article/details/143122449

相关文章

  • GD-WLAN登录页面抓包及curl模拟方法
    摘要:校园网Web认证界面点击登录时会发送一个Post请求,密码使用时间戳作为密钥进行RC4加密(后经验证,时间戳可为任意值),服务器根据密钥解密并验证账户与密码,验证通过便可以正常上网。因而可以采用curl等工具模拟Post请求,完成登录。实现路由器、服务器、手机、平板等快捷联网。......
  • ES6-模块化
    介绍模块化是指将一个大的程序文件,拆分成许多小的文件,然后将小文件组合起来。模块化的好处防止命名冲突代码复用高维护性ES6之前的模块化规范有CommonJS=>NodeJS、BrowserifyAMD=>requireJSCMD=>seaJSES6模块化语法模块功能主要由两个命令构成:export和im......
  • 信息学奥赛复赛复习18-CSP-J2023-01小苹果-向上取整、向下取整、模拟算法
    PDF文档公众号回复关键字:202410211P9748[CSP-J2023]小苹果[题目描述]小Y的桌子上放着n个苹果从左到右排成一列,编号为从1到n。小苞是小Y的好朋友,每天她都会从中拿走一些苹果。每天在拿的时候,小苞都是从左侧第1个苹果开始、每隔2个苹果拿走1个苹果。随......
  • jsp仿小鹅通知识付费微网站r8aag--程序+源码+数据库+调试部署+开发环境
    本系统(程序+源码+数据库+调试部署+开发环境)带论文文档1万字以上,文末可获取,系统界面在最后面。系统程序文件列表用户,课程,培训课程,订单信息,作品分类,作品信息,书籍类型,书籍信息,购买订单开题报告内容一、研究背景与意义随着互联网的快速发展,知识付费已成为一种新兴的......
  • SpringBoot整合easy-es
    一、easy-es简介‌‌EasyES是一款基于Elasticsearch官方提供的RestHighLevelClient开发的ORM框架,旨在简化开发流程并提高效率。‌EasyES在保持RestHighLevelClient原有功能的基础上进行增强,而不做任何改变。它采用与Mybatis-Plus相似的语法,使得开发者可以无缝迁移至EasyES,无......
  • mybatis - [09] 动态SQL
    题记部分 一、if&test如果id,name,age不为空,则按照指定的值进行查询。如果这三者都是空(null和空字符串),则该sql执行结果为全表查询的结果集。<selectid="getUserByUser"parameterTytpe="vo.User"resultMap="userResultMap">selectin,name,agefrom......
  • 【2024-10-19】连岳摘抄
    23:59心灵开朗的人,面孔也是开朗的。                                                 ——席勒一个人,总有他的职责,把职责划分清楚,有时候烦恼也就消失了。所以孔子说,......
  • schedule-执行周期性任务
    模块介绍该模块主要用于python的任务调度,使用简便友好的python语法定期运行python函数或者一些其他的调用对象,这个模块就类似于windows的任务计划和linux的crontab,都是用于在服务器上周期性执行某段python脚本。相较于linux的crontab对比:schedule模块支持以秒为单位的周期性任......
  • 【2024-10-18】安排二宝
    20:00前途很远,也很暗,然而不要怕。不怕的人的面前才有路。                                                 ——XX如果我哪天写日记特别困难,需要埋头去寻找这一天内有......
  • CSS速刷 - CSS动画
    作用:引起注意、愉悦感、反馈、掩饰(加载过程)transition动画补间动画,中间过程可以计算出来。transition:width1s:意味动画属性是width,动画时间是1秒。delay:动画延迟几秒再开始transition-timing-function缓动函数:可以自己定制。关键帧动画animationanimatio......