JavaEE——多线程1(超详细版)

目录

基础理论知识:

进程(process):

1.如何管理进程:

2.PCB(进程控制块):

线程(Thread):

一.创建线程

1.继承thread类

2.实现Runnable接口

3.匿名内部类创建Thread子类对象

4.匿名内部类创建Runnable子类对象

5.使用lambda表达式创建Runnable子类对象

二.Thread常用的构造方法

三.Thread的几个常见属性

四.启动一个线程--start()

五.中断一个进程(两种方式)

1.通过共享标志位进行沟通通知

2.调用Interrupt()方法来通知

六.等待一个进程--join()

七.获取当前线程引用

八.休眠当前线程--sleep()

线程的状态


基础理论知识:

1.冯.诺依曼体系结构

cpu(中央处理器)(可用各种算术运算,逻辑判断的"通用计算机芯片"),存储器(硬盘 ,内存...),输入设备(键盘,鼠标...),输出设备(显示器...)

硬盘和内存对比:

硬盘:读写速度慢,存储空间大,成本低,断电不丢失数据

内存:读写速度快,存储空间小,成本高,断电丢失数据

2.指令:指导CPU进行工作的命令

一条指令(程序)执行过程:

a.读取指令

b.解析指令

c.执行指令

**3.寄存器:**在CPU上存储数据的模块(CPU存储数据实际是通过寄存器)(在CPU上执行指令,进行各种运算存储临时数据)

CPU的寄存器和内存的对比:

CPU寄存器·:读写速度快,存储空间小,成本高,断电丢失数据

内存:读写速度慢,存储空间大,成本低,断电丢失数据

进程(process):

一个运行起来的程序

1.如何管理进程:

使用结构体,进程控制块(PCB),将多个进程组织起来

2.PCB(进程控制块):

a.pid(进程id):进程标识符

b.内存指针:进程需要知道需要执行的指令在哪,指令依赖的数据又在哪

我们可以得出:进程运行过程中,需要依赖内存资源

c.文件描述符表:进程运行,依赖到网盘 ,网卡等相关资源;进程运行,执行指令,依赖CPU,也消耗CPU资源

我们又可以得出结论:进程是操作系统资源分配的基本单位

以下也是进程调度

d.进程状态(这里只提到了两个)

--》就绪状态:随叫随到

--》阻塞状态:进程当前不适合在CPU上执行

e.进程优先级:有些进程,优先级更高,可以得到的CPU资源更多

f.进程上下文:保存上下文 恢复上下文

g.进程的记账信息

线程(Thread):

可以说是轻量级的进程,一个线程就是一个"执行流"

1.为什么要用线程

a."并发编程"成为刚需

b.线程比进程更轻量:创建线程比创建进程更快,销毁线程比销毁进程更快,调度线程比调度进程更快

!!!2.进程和线程的区别!!!

A.进程是包含线程的(每个进程至少含有一个线程,即主线程)

B.进程与进程之间是不共享资源空间的,同一个进程的多个线程之间是共享同一个资源空间的

C.进程是操作系统资源分配的最小单位,线程是操作系统调度的最小单位

D.一个进程挂了不会影响其他进程,同一个进程的一个线程挂了会一并带走其他线程(整个进程崩溃

一.创建线程

1.继承thread类

要子类重写父类run方法

java 复制代码
package Thread;


import static java.lang.Thread.sleep;

public class demo {
    //第二个线程
    public static class mythread extends Thread{
        @Override
        public void run() {//子类对父类方法的重写
            while(true){
                System.out.println("这是一个thread线程...");
                try {
                    sleep(1000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }

        }
    }
    //主线程
    public static void main(String[] args) {
        mythread thread=new mythread();//向上转型
        thread.start();//真正创建了一个线程
        while (true){
            System.out.println("这是一个主线程...");
            try {
                sleep(1000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }

    }
}

2.实现Runnable接口

需要重写run方法

java 复制代码
package Thread;

import static java.lang.Thread.sleep;

public class demo2 {
    public static class myrunnnable implements Runnable{
        @Override
        public void run() {
            while (true) {
                System.out.println("这是一个runnable实现的线程");
                try {
                    sleep(1000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }

        }
    }

    public static void main(String[] args) {
        //方式一
        Thread thread=new Thread(new myrunnnable());
        //方式二
        // Runnable runnable=new myrunnnable();
        //Thread thread=new Thread(runnable);
        thread.start();
        while (true){
            System.out.println("这是一个mian线程...");
            try {
                sleep(1000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    }
}

3.匿名内部类创建Thread子类对象

java 复制代码
package Thread;

import static java.lang.Thread.sleep;

public class dome3 {
    public static void main(String[] args) {
        Thread thread=new Thread() {
            @Override
            public void run() {
                while (true) {
                    System.out.println("这是匿名内部类创建thread子类对象实现的线程");
                    try {
                        sleep(1000);
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                }

            }
        };
        thread.start();
        while (true) {
            System.out.println("这是mian实现的线程");
            try {
                thread.sleep(1000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    }
}

4.匿名内部类创建Runnable子类对象

java 复制代码
package Thread;

public class dome4{
        public static void main(String[] args) {
            Thread thread=new Thread(new Runnable() {
                @Override
                public void run() {
                    while (true) {
                        System.out.println("这是匿名内部类创建Runnable子类对象实现的线程");
                        try {
                            Thread.sleep(1000);
                        } catch (InterruptedException e) {
                            throw new RuntimeException(e);
                        }
                    }
                }
            });
            thread.start();
            while (true) {
                System.out.println("这是mian实现的线程");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        }
    }

5.使用lambda表达式创建Runnable子类对象

java 复制代码
package Thread;

public class dome5 {
    public static void main(String[] args) {
        //()->{}创建了一个匿名函数式接口的子类,并创建对应的实例,重写里面的方法
        Thread thread=new Thread(()->{ 
                while(true) {
                    System.out.println("函数式接口调用");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
        });
        thread.start();
        while (true) {
            System.out.println("这是mian实现的线程");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    }
}

二.Thread常用的构造方法

1.Thread()//创建线程对象

2.Thread(Runnable target)//使用Runnable接口创建子类对象

3.Thread(String name)//创建线程对象,并命名

4.Thread(Runnable target,String name)//使用Runnable接口创建子类对象,并命名

三.Thread的几个常见属性

ID 获取方法:getId()

名称 获取方法:getName()

状态 获取方法:getState()

优先级 获取方法:getPriority()

是否后台线程 获取方法:isDaemon()

什么是前台线程?什么是后台线程?

前台线程可以理解为 自己创建的线程和main主线程;可以用setDaemon方法去修改;前台线程有多个时,全部结束,线程结束

后台线程可以理解为 除了前台线程的线程,不影响线程的结束

JVM会在一个进程中的所有非后台线程结束后,才会结束运行

是否存活 获取方法:isAlive()

可以理解为run方法是否运行结束

是否中断 获取方法:isInterrupt()

四.启动一个线程--start()

是JVM提供的方法,本质是调用操作系统的API

调用start才是在操作系统底层中创建了一个线程

!!!一个Thread对象只能start一次!!!

五.中断一个进程(两种方式)

也可以理解为终止一个进程,就是让线程的入口方法尽快结束

1.通过共享标志位进行沟通通知

java 复制代码
package Thread;

public class dome6 {
    public static volatile boolean flag=false;
    public static void main(String[] args) {
        Thread thread=new Thread(()->{
            while(!flag) {
                System.out.println("这个线程正在进行...");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        });

        thread.start();

        try {
            Thread.sleep(1000*5);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        System.out.println("不能运行了,累了...");
        flag=true;
    }
}

2.调用Interrupt()方法来通知

java 复制代码
package Thread;

public class demo7 {
        public static void main(String[] args) {
            Thread thread=new Thread(()->{
                while(!Thread.interrupted()) {
                    System.out.println("这个线程正在进行...");
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                }
            });

            thread.start();

            try {
                Thread.sleep(1000*5);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            System.out.println("不能运行了,累了...");
            Thread.interrupted();
        }
    }

思考:为什么代码又继续运行了呢?

是因为异常引起的,当我们调用sleep/wait/join方法,会以异常的方式通知,清除中断标志位,所以,取决于我们在处理异常的时候,关键在于catch代码块

如下代码:

java 复制代码
package Thread;

public class demo7 {
    public static void main(String[] args) {
        Thread thread=new Thread(()->{
            while(!Thread.currentThread().isInterrupted()) {
                //while(Thread.interrupted()){//两种方法都可以
                System.out.println("这个线程正在进行...");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                    break;
                    //break;            立即结束
                    //什么也不写          不终止
                    //写其他逻辑再break   稍后终止
                }
            }
        });
        thread.start();
        try {
            Thread.sleep(1000*5);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        System.out.println("不能运行了,累了...");
        thread.interrupt();
    }
}

六.等待一个进程--join()

可以理解为多个线程,线程结束的先后顺序

java 复制代码
package Thread;

public class demo8 {
    public static void main(String[] args) {
        Thread thread1 = new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                System.out.println("线程1在运行...");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        });
        Thread thread2 = new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                System.out.println("线程2在运行...");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        });
        thread1.start();
        thread2.start();
        thread.join();
        thread.join();

    }
}

我们使用再次使用join方法的正确位置后再观察(先start再join)

java 复制代码
package Thread;

public class demo8 {
    public static void main(String[] args) throws InterruptedException {
        Thread thread1 = new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                System.out.println("线程1在运行...");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        });
        Thread thread2 = new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                System.out.println("线程2在运行...");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        });
        thread1.start();
        thread1.join();
        thread2.start();
        thread2.join();

    }
}

同时,join()在括号中也可以添加参数,避免死等的情况(即超时等待)

七.获取当前线程引用

**public static Thread currentThread()**返回当前线程对象的引用

八.休眠当前线程--sleep()

实际休眠时间大于等于设置参数的时间(由于CPU的调度)

它是Thread类中的方法,可以进行超时等待

线程的状态

1.NEW:安排了⼯作,还未开始行动(也可以理解为new一个对象,但是没有进行start操作)

  1. RUNNABLE:可⼯作的,又可以分成正在⼯作中和即将开始⼯作(也可以理解为就绪状态 a.正在CPU上工作 b.随时准备到CPU上工作)

  2. BLOCKED :这⼏个都表示排队等着其他事情(由于锁导致的阻塞)

  3. WAITING:这⼏个都表示排队等着其他事情(没有超时的等待(死等))

  4. TIMED_WAITING:这⼏个都表示排队等着其他事情(指定时间的阻塞(线程阻塞不参与CPU的调度),超时等待)

  5. TERMINATED:⼯作完成了(可以理解为线程结束了,但是thread对象还在)

!!!前面学到的isAlive()可以理解为不是new和terminated状态都是活着的!!!

多线程内容,先到这里,有问题欢迎评论区留言讨论,请同学们多多练习相关知识!!!看我后续内容

相关推荐
tuokuac8 小时前
依赖spring-cloud-starter-gateway与spring-cloud-gateway-dependencies的区别
java·gateway
seabirdssss8 小时前
JDK 11 环境正确,端口未被占用,但是运行 Tomcat 11 仍然闪退
java·开发语言·tomcat
努力学算法的蒟蒻8 小时前
day03(11.1)——leetcode面试经典150
java·算法·leetcode
Mr YiRan9 小时前
SYN关键字辨析,各种锁优缺点分析和面试题讲解
java·开发语言
Zhang青山9 小时前
028.爬虫专用浏览器-抓取#shadowRoot(closed)下
java·后端
bug攻城狮9 小时前
SpringBoot响应封装:Graceful Response vs 自定义通用响应类选型指南
java·spring boot·后端·http
m0_736927049 小时前
Spring Boot项目中如何实现接口幂等
java·开发语言·spring boot·后端·spring·面试·职场和发展
再睡一夏就好10 小时前
【C++闯关笔记】使用红黑树简单模拟实现map与set
java·c语言·数据结构·c++·笔记·语法·1024程序员节
oak隔壁找我10 小时前
ShardingJdbc配置说明
java·后端