javaEE-多线程编程-3

发布于:2024-12-21 ⋅ 阅读:(9) ⋅ 点赞:(0)

目录

java 常见的包 :

回调函数:

什么是线程:

第一个线程:

验证多线程执行:

内核:

调用sleep()方法:

执行结果分析:

线程创建的几种方式:

1.继承Thread类,重写run()方法.

2.实现Runnable接口,重写run()方法.

3.继承Thread类,重写run()方法.但使用匿名内部类

4.实现Runnable接口,重写run()方法,但使用匿名内部类:

5.使用lambda表达式(推荐)

Thread的构造方法

Thread的常用的属性:

1.ID:jvm自动分配的身份标识,不同线程不会重复

2.名称是调试的时候用到的

3.状态:

4.优先级:

5.是否后台线程:

6.是否存活:


在写代码的时候,可以用多进程编程,也可以用多线程编程.

多进程编在java中不太推荐,因为与多进程编程相关的api,在java标准库中都没有提供.

系统提供了多线程编程的api,在java标准库中,把这些api都封装了,在代码中可以直接使用.

并且在面对频繁创建和销毁进程的时候,多线程编程具有非常大的优势.效率非常高.

java 常见的包 :

1. java.lang:系统常用基础类(String、Object),此包从JDK1.1后自动导入。

2. java.lang.reflect:java 反射编程包;

3. java.net:进行网络编程开发包。

4. java.sql:进行数据库开发的支持包。

5. java.util:是java提供的工具程序包。(集合类等) 非常重要

6. java.io:I/O编程开发包

Thread类就是java.long包下的类,不需要引入包就能直接使用.

一个.java文件中,只能有一个类被public修饰,若该类没有被public修饰,就只能在该包中,被别的类引用.

一个进程至少会有一个线程,该进程的第一个线程就是main线程,也就是主线程的入口方法.

回调函数:

回调函数就是一个通过函数指针调用的函数。如果你把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用来调用其所指向的函数时,我们就说这是回调函数。回调函数不是由该函数的实现方直接调用,而是在特定的事件或条件发生时由另外的一方调用的,用于对该事件或条件进行响应。

简单来说:回调函数就是把一段代码,向传参一样,传递给其它代码,这段代码会在某个时刻被调用执行.这就叫做回调.

回调函数就是一个通过函数指针调用的函数。如果你把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用来调用其所指向的函数时,我们就说这是回调函数。

回调函数不是由该函数的实现方直接调用,而是在特定的事件或条件发生时由另外的一方调用的,用于对该事件或条件进行响应。如果代码立即被执行就称为 同步回调,如果过后再执行,则称之为 异步回调。

什么是线程:

⼀个线程就是⼀个"执⾏流".每个线程之间都可以按照顺序执⾏⾃⼰的代码.多个线程之间"同时"执⾏ 着多份代码

第一个线程:

创建一个Thread01类,让该类继承Thread类,重写Thread类的run()方法,

在主函数中实例化该类,调用该类的start()方法,这样,不需要我们手动调用run()方法,待线程创建好后,会在合适的时机被jvm自动调用.(这种风格的函数,就被称为"回调函数"callback)

调⽤start()⽅法,才真的在操作系统的底层创建出⼀个线程.

package Thread_;

class MyThread01 extends Thread{
    @Override
    public void run() {//重写run()方法
        //run()方法就是该线程的入口
        while (true){
            System.out.println("Thread run");
        }
    }
}
public class Thread01 {
    public static void main(String[] args) {
        Thread thread01 = new MyThread01();
        thread01.start();//必须要调用start()方法,才能开启线程
        while(true){
        System.out.println("main ");
        }
    }
}

当引入多线程之后,代码中就可以同时具备多个执行流了.

验证多线程执行:

可以通过jdk/bin/jconsloe.exe文件看进程状态.

先运行自己写的代码,让后进行下面的操作

内核:

操作系统的内核是操作系统最核心的功能模块.(管理硬件,给软件提供稳定的运行环境)

简单来说:内核态是非常重要的,不允许用户修改的,划分出用户态和内核态也是目的是为了稳定,以防自己的应用程序把硬件设备或软件资源给搞坏了.

操作系统=内核+应用程序.

一旦开始执行,线程就会飞快的循环起来,使cpu占用率较高,进一步提高电脑的功耗,为了不让循环跑的那么快,可以在循环体中设置Thread类的sleep()方法,让每隔一定时间运行一次,这样就能降低电脑的功耗.

调用sleep()方法:

sleep()方法调用时,会有受查异常,需要手动抛出.

Mythread类中的run()方法内的异常抛出只能以try-catch的形式,不能以throws的形式,因为run()方法是重写父类的方法,父类没有throws这个异常,若加上throws,就修改了方法签名,因此,子类重写run()方法的时候,也就只能以try-catch的形式抛出异常了.

以try-catch的形式抛出异常:

以throws的形式抛出异常:

时间转换单位:

1s=1000ms(毫秒)

1ms=1000(微妙)

1um=1000nm(纳秒)

上面的第一个线程代码,可以看出,main()方法中有一个死循环,在Thread01类的run()方法中也有一个死循环,一般来说,一个代码中出现两个死循环,只会运行一个,但实际上这两个死循环中的代码都被运行了,这就说明是两个线程在同时执行.

执行结果分析:

从执行的结果上可以看出,thread和main是随机交替出现的,也就是说,每隔一秒钟,thread和main被执行一次,但是谁先被执行,是无法确定的.

但能看出,每次最开始执行的时候,都是main第一次被执行,这是因为在创建MyThrow01线程的时候,还是会有开销的,这个开销比创建进程要低很多(但也不是没有),就是这一点点的开销,使得main线程每次在最开始的时候先执行.

(注意:main线程是第一线程)

线程创建的几种方式:

1.继承Thread类,重写run()方法.

也就是上面的这种方式.

package Thread_;

class MyThread01 extends Thread{
    @Override
    public void run() {
        //run()方法就是该线程的入口
        while (true){
            System.out.println("Thread run");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    }
}
public class Thread01 {
    public static void main(String[] args) {
        Thread thread01 = new MyThread01();
        thread01.start();//必须要调用start()方法,才能开启线程
        while(true){
        System.out.println("main ");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    }
}

2.实现Runnable接口,重写run()方法.

Runnable接口需要搭配Thread类使用,才能真正在系统中创建出线程来
package Thread_;

class MyThread02 implements Runnable{
    @Override
    public void run() {
        while(true){
            System.out.println("Thread01 run()");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    }
}
public class Thread02 {
    public static void main(String[] args) {
        MyThread02 runnable = new MyThread02();
        Thread thread1 = new Thread(runnable);//Runnable接口需要搭配Thread类使用,才能真正在系统中创建出线程来
        thread1.start();
        while(true){
            System.out.println("main()");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }

    }
}

3.继承Thread类,重写run()方法.但使用匿名内部类

Thread t=new Thread(){//这里写的{}表示要定义一个类,并且这个新类继承Thread,
                        //且没有名字,用一次就不能再用了
                       //{}中可以定义新类的属性和方法
                        //此处的目的就是重写父类Thread的run()方法.
    @Override
    public void run() {
        while(true){
            System.out.println("Thread run03");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    }
};

这里的thread变量名指向的是新创建的Thread的匿名内部类(子类),而不是Thread类.

4.实现Runnable接口,重写run()方法,但使用匿名内部类:

Thread构造方法的参数,传了Runnable的匿名内部类的实例.

5.使用lambda表达式(推荐)

lambda表达式原理:

Thread thread=new Thread(()->{    //这里的()是形参列表,在这里,不需要传参数
                                 //()的前面应该有个函数名,这里是匿名的,所以没有名字
                                //->后面的{}是方法体,
    while(true){
        System.out.println("Thread run5");
        try{
        Thread.sleep(1000);
        }catch(InterruptedException o){
             throw new RuntimeException(e);
        }
    }
});

这里的规则是方法不能脱离类单独存在.

这几种方法都是等价的,作用都相同.

Thread的构造方法

1.构造方法:

前两个在前面创建线程的时候都提到了,

第三,四个:给线程起个名字:

在jcolsole.exe中就能看到线程名字被修改

自己创建的线程默认是按照Thread-0 1 2 ...创建的,线程之间的名字可以重复,起名字是为了方便调试.

Thread的常用的属性:

1.ID:jvm自动分配的身份标识,不同线程不会重复

2.名称是调试的时候用到的

3.状态:

线程有不同的状态:就绪状态,阻塞状态

4.优先级:

优先级⾼的线程理论上来说更容易被调度到,线程的优先级在java中,效果不是很明显

5.是否后台线程:

和后台线程相对应的是前台线程,当一个进程开始运行的时候,只有前台线程都运行结束,后台线程才会结束.

JVM会在⼀个进程的所有⾮后台线程结束后,才会结束运⾏。

目前代码创建的线程都是前台线程,都会阻止线程的结束,当全部都执行完了,才会结束整个进程.

设置线程为后台线程:在start之前setDaemon(true);此时,该线程就为后台线程

在start之前,给thread线程设置成了后台线程,在main函数之后,休眠2秒,thread线程就执行了2秒,打印了两遍,main线程结束了,thread线程为后台线程,也就结束了.

6.是否存活:

表示内核中的线程是否还存活,在start之前,还没有开始线程,isAlive为false,

只有调用了start,线程才开始执行,isAive才为true.

2s之后,run()已经结束,isAlive为false.


网站公告

今日签到

点亮在社区的每一天
去签到