多线程的实现和成员方法

 所属专栏:Java学习       

在这里插入图片描述

 

 

1. 多线程的概念

线程线程是操作系统能够进行运算调度的最小单位,它被包含在进程之中,是进程的实际运作单位

下面这些每一个能够运行的软件就是一个进程

进程在系统中是通过PCB这样的结构体来描述,通过链表的形式来组织的,线程也同样是通过PCB来描述的,一个进程就是一组PCB,也就是一个进程包含了多个线程,每一个线程都可以独立的到CPU上执行

对于一个可执行程序,运行时操作系统就会创建进程,给这个程序分配各种系统资源(CPU,内存,硬盘,网络带宽...),同时也会在这个进程中创建多个线程,这些线程再到CPU上调度执行,同一个进程中的这些线程,是共用一份系统资源的

线程相比于进程更加轻量,省去了创建线程资源分配的过程和销毁线程释放资源的过程

并发:在同一时刻,有多个指令在单个CPU上交替执行

并行:在同一时刻,有多个指令在多个CPU上同时执行

也就是,有多个核心,每个核心都可以执行一个线程,这些核心之间的执行过程是同时执行的,也就是并行,此外,一个核心也可以按照“分时复用”,来切换多个线程,虽然说多个线程是一个接一个执行的,由于调度速度足够快,就好像同时进行一样,这也叫做并发


进程和线程的概念和区别:

  1. 进程包括线程,一个进程里面可以有单个或多个线程,但不能没有
  2. 进程是系统资源分配的最小单位,线程是系统调度执行的最小单位
  3. 同一个进程里的线程之间共用一份系统资源
  4. 线程是当下实现并发编程的主流方式,通过多线程,就可以充分利用好多核CPU,但是线程也不是越多越好,线程数目达到一定程度,把多个核心充分利用之后,此时再增加线程,就无法提升效率了,还可能影响效率(线程调度也是有开销的)
  5. 多个线程之间可能会相互影响,如果出现线程安全问题,一个线程抛出异常,也可能会把其他线程一起带走
  6. 多个进程之间一般不会相互影响,一个进程崩溃了,不会影响到其他进程(进程的隔离性)

2. 多线程的实现方式

2.1. 继承Thread类的方式进行实现

实现方式:

1. 定义一个类,继承Thread

2. 重写run方法

3. 创建子类对象,并启动线程

 

public class MyThread extends Thread{
    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            System.out.println(getName() + "hello");
        }
    }
}
public class ThreadDemo1 {
    public static void main(String[] args) {
        MyThread t1 = new MyThread();
        MyThread t2 = new MyThread();
        //设置线程对象名称
        t1.setName("线程一:");
        t2.setName("线程二:");
        //开启线程
        t1.start();
        t2.start();
    }
}

在开启线程之后,会交替执行线程一和线程二

在上面的方法中,run方法没有手动的进行调用,最终也执行了,像这样的没有手动调用,最终这个方法被系统,库或者框架进行调用了,这种方法就称为“回调函数”

当调用start()方法时,会启动一个新的线程来执行run()方法中的代码。如果不调用start()方法,仅仅创建了线程对象,并不会创建新的线程执行任务。

也可以通过内部类的方式实现:

public class ThreadDemo1 {
    public static void main(String[] args) {
        Thread thread = new Thread() {
            @Override
            public void run() {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("Hello");
            }
        };
        thread.start();
        System.out.println("main");
    }
}

使用匿名内部类的形式,一般就是一次性的类,比较方便,内聚性也比较好

2.2. 实现Runnable接口的方式进行实现

实现方式:

  1. 自定义一个类,实现Runnable接口
  2. 重写里面的Run方法
  3. 创建自定义类的对象
  4. 创建一个Thread类的对象,并开启线程
public class MyRun implements Runnable{
    @Override
    public void run() {
        for(int i = 0;i < 10;i++){
            //获取当前线程对象
            Thread t = Thread.currentThread();
            System.out.println(t.getName() + "hello");
        }
    }
}
public class ThreadDemo2 {
    public static void main(String[] args) {
        //创建MyRun对象,表示多线程要执行的任务
        MyRun myRun = new MyRun();
        //创建线程的对象
        Thread t1 = new Thread(myRun);
        Thread t2 = new Thread(myRun);
        //给线程设置名字
        t1.setName("线程一:");
        t2.setName("线程二:");
        //开启线程
        t1.start();
        t2.start();
    }
}

开启线程之后也是交替执行线程一和线程二

还可以通过匿名内部类的方式来实现:

public class ThreadDemo2 {
    public static void main(String[] args) {
        Thread thread = new Thread(new Runnable() {
            int n = 10;
            @Override
            public void run() {
                while (n-- != 0) {
                    System.out.println("hello");
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        });
        thread.start();
        while (true) {
            System.out.println("main");
            Thread.sleep(1000);
        }
    }
}

既然可以使用内部类的形式了,那么也可以用lambda表达式来进行简化:

public class ThreadDemo2 {
    public static void main(String[] args) throws InterruptedException {
        Thread thread = new Thread(()->{
            while (true){
                System.out.println("thread");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        thread.start();
        while (true){
            System.out.println("main");
            Thread.sleep(1000);
        }
    }
}

2.3. 利用Callable接口和Future接口方式实现

实现方式:

1. 创建一个类MyCallable实现Callable接口

2. 重写call方法(返回值代表多线程运行的结果)

3. 创建MyCallable对象(表示多线程要执行的任务)

4. 创建FutureTask对象(作用管理多线程运行的结果)

5. 创建Thread类的对象并启动(表示线程)

import java.util.concurrent.Callable;

public class MyCallable implements Callable<Integer> {
    @Override
    public Integer call() throws Exception {
        int sum = 0;
        for (int i = 0; i < 100; i++) {
            sum += i;
        }
        return sum;
    }
}
public class ThreadDemo3 {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        //创建MyCallable对象(表示多线程要执行的任务)
        MyCallable myCallable = new MyCallable();
        //创建FutureTask对象(作用管理多线程运行的结果)
        FutureTask<Integer> ft = new FutureTask<>(myCallable);
        //创建Thread类的对象并启动(表示线程)
        Thread t1 = new Thread(ft);
        t1.start();
        Integer res = ft.get();
        System.out.println(res);
    }
}

2.4. 三种实现方式对比

优点

缺点

继承Thread类

编程简单,可以直接使用Thread中的方法

可拓展性差,不能再继承其他类

实现Runnable接口

拓展性强,实现该接口同时还可以继承其他类

编程相对复杂,不能直接使用Thread中的方法

实现Callable接口

3. 常见的成员方法

3.1. getName()和setName()

对于setName()来说,如果没有给线程设置名称,也是有默认的名字的,格式:Thread - X(x序号从0开始)

根据Thread类的空参构造可以看出,在创建对象时的默认名称格式

那怎么在自定义类中创建对象时就传入对象名称进行构造呢?

在多线程第一种实现方式中,自定义的类继承了Thread类,但是构造方法并没有继承,所以还需要在自定义类中手动的去实现构造方法

3.2. currentThread()和sleep()

currentThread可以获取当前线程的对象

当JVM虚拟机启动之后,会自动开启多条线程,其中一条线程就叫做main线程,作用就是调用main方法,并执行里面的代码

sleep()是让线程休眠指定的时间,单位是毫秒,哪条线程执行到这个方法,那么哪条线程就会休眠,时间到了之后会继续执行下面的操作

这里的异常可以直接抛出

再来看MyThread类,这里的异常处理不能使用Throw抛出了,因为父类Thread没有抛出,这里只能使用try-catch处理异常

3.3. getPriority()和setPriority()

3.3.1. CUP的调度方式

CUP的调度方式是有两种的:分为抢占性调度和非抢占性调度

抢占式调度是一种允许高优先级线程中断低优先级线程的执行,从而立即获得CPU资源的调度方式。在这种模式下,操作系统会定期检查线程的优先级,并根据需要切换线程的执行, 对线程的访问是随机的

非抢占式调度是一种允许线程独占CPU直到其主动放弃或执行完毕的调度方式。在这种模式下,线程的执行时间由线程本身控制,调度器不会中断正在执行的线程,轮流执行线程

3.3.2. 优先级

来看Thread类中优先级的设置,最小为1,最大为10,默认是5,优先级越高抢占到CUP的概率越高,只是说概率高,并不是优先级高的就肯定比优先级低的要先抢占到CUP

public class ThreadTest2 {
    public static void main(String[] args) {
        //创建线程要执行参数的对象
        MyRunable myRunable = new MyRunable();
        //创建线程对象
        Thread thread1 = new Thread(myRunable,"线程一");
        Thread thread2 = new Thread(myRunable,"线程二");

        //获取线程优先级
        System.out.println(thread1.getPriority());
        thread2.setPriority(10);
        System.out.println(thread2.getPriority());

        System.out.println(Thread.currentThread().getPriority());
    }
}

3.4. setDaemon()

设置为守护(后台)线程也类似于备胎线程,当前台线程执行完毕之后,守护线程会陆续结束(并不是立即结束)

public class ThreadTest3 {
    public static void main(String[] args) {
        MyThread1 thread1 = new MyThread1();
        MyThread2 thread2 = new MyThread2();
        thread1.setName("女神");
        thread2.setName("备胎");
        //把第二个线程设置为守护线程
        thread2.setDaemon(true);
        thread1.start();
        thread2.start();
    }
}

应用场景:例如在聊天软件中,当打开聊天窗口之后,开启聊天窗口的线程和发送文件的线程,这时就可以把发送文件设置为守护线程,当聊天窗口关闭之后,守护线程也就没有存在的必要了

3.5. isAlive()

代码中,创建的new Thread 对象,生命周期和内核中实际的线程是不一样的,可能就会出现这种情况:Thread对象仍然存在,但是内核中的线程不存在了的情况(但不会出现相反的情况),原因就是调用start()之前,还没有创建线程,或者是run执行完了,内核的线程就没有了,但是Thread对象还存在

isAive()是判断线程是否存活的,返回一个boolean值

public class ThreadDemo3 {
    public static void main(String[] args) throws InterruptedException {
        Thread thread = new Thread(()->{
            for (int i = 0; i < 3; i++) {
                System.out.println("thread");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        System.out.println(thread.isAlive());
        thread.start();
        System.out.println(thread.isAlive());
        Thread.sleep(2000);
        System.out.println(thread.isAlive());
    }
}

由于线程之前调度顺序是不确定的,休眠结束后谁先执行不一定(也并不是都是一半的概率,这种概率会随着系统的不同和代码运行环境的不同,都可能存在差异)

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/872965.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Datawhle X 李宏毅苹果书AI夏令营深度学习笔记之——卷积神经网络的前世今生

一、卷积神经网络简介 卷积神经网络&#xff08;Convolutional Neural Network, CNN&#xff09;是一种深度学习模型&#xff0c;尤其擅长处理图像和视频等高维度的数据。CNN 通过模仿人类视觉系统的工作方式&#xff0c;自动学习数据中的空间层次结构&#xff0c;使得它在计算…

传统CV算法——特征匹配算法

Brute-Force蛮力匹配 Brute-Force蛮力匹配是一种简单直接的模式识别方法&#xff0c;经常用于计算机视觉和数字图像处理领域中的特征匹配。该方法通过逐一比较目标图像中的所有特征点与源图像中的特征点来寻找最佳匹配。这种方法的主要步骤包括&#xff1a; 特征提取&#xff…

将Google Chrome或Microsoft Edge浏览器的地址栏隐藏的方法

将Google Chrome或Microsoft Edge浏览器的地址栏隐藏的方法 目标效果示范 我们以百度首页为例&#xff0c;普通模式启动的页面通常会显示地址栏&#xff0c;如下图所示&#xff1a; 而本文要实现的效果是隐去地址栏和书签栏&#xff08;如果有的话&#xff09;&#xff0c;无…

[C#]国密SM2算法加解密字符串加密解密文件

【算法介绍】 国密SM2算法是一种由中国国家密码管理局发布的非对称加密算法&#xff0c;基于椭圆曲线密码学体系&#xff0c;具有较高的安全性和效率。该算法主要用于数字签名、密钥交换和公钥加密等场景&#xff0c;其安全性主要基于椭圆曲线离散对数问题的难解性。 SM2算法…

网络编程(三次握手四次挥手)

【1】三次握手四次挥手 1》三次握手 第一次握手都又客户端发起&#xff0c;在TCP/IP协议中&#xff0c;TCP协议提供可靠的连接服务&#xff0c;采用三次握手建立一个连接。服务器必须准备好接收外来的链接&#xff0c;这通过调用socket、bind和listen函数来完成&#xff0c;称…

小阿轩yx-Kubernertes日志收集

小阿轩yx-Kubernertes日志收集 前言 在 Kubernetes 集群中如何通过不同的技术栈收集容器的日志&#xff0c;包括程序直接输出到控制台日志、自定义文件日志等 有哪些日志需要收集 日志收集与分析很重要&#xff0c;为了更加方便的处理异常 简单总结一些比较重要的需要收集…

https和harbor仓库跟k8s

目录 https 做证书 harbor仓库 https https是加密的http&#xff0c;它的端口是443&#xff0c;它的协议是tcp协议。建立连接和普通的tcp是一样的&#xff0c;都是三次握手和四次挥手&#xff0c;但是它三次握手之后有一个步骤&#xff1a;SSL或者TLS握手的过程&#xff0c…

kubernetes集群部署Confluence 7.2.0+mysql 5.7(自测有效)

背景介绍&#xff1a; Confluence是一个专业的企业知识管理与协同软件。使用简单&#xff0c;但它强大的编辑和站点管理特征能够帮助团队成员之间共享信息、文档协作、集体讨论&#xff0c;信息推送。 这里介绍的使用的是Confluence 7.2.0版本的。 一、在kubernetes集群部署 1…

【C++】手动实现nique_ptr智能指针

1、自己实现封装了一个智能指针&#xff0c;并且使用了模板 目录 代码实现&#xff1a; 输出结果如下&#xff1a; 代码实现&#xff1a; #include <iostream>using namespace std;template <typename T> class UniquePtr { private:T *ptr;public://默认构造函…

H5 响应式精品网站推荐导航源码

源码名称&#xff1a;响应式精品网站推荐导航源码 源码介绍&#xff1a;一款响应式精品网站推荐导航源码&#xff0c;可以自己修改代码替换图标图片和指向网址。背景图支持自动替换&#xff0c;背景图可以在img.php中修改 需求环境&#xff1a;H5 下载地址&#xff1a; http…

【MADRL】多智能体深度确定性策略梯度(MADDPG )算法

本篇文章是博主强化学习RL领域学习时&#xff0c;用于个人学习、研究或者欣赏使用&#xff0c;并基于博主对相关等领域的一些理解而记录的学习摘录和笔记&#xff0c;若有不当和侵权之处&#xff0c;指出后将会立即改正&#xff0c;还望谅解。文章分类在强化学习专栏&#xff1…

Java专栏介绍

专栏导读 在当今这个技术飞速发展的时代&#xff0c;Java作为一门成熟且广泛应用的编程语言&#xff0c;一直是软件开发领域的中坚力量。本“Java技术”专栏旨在帮助读者深入理解Java编程语言的精髓&#xff0c;掌握其核心概念与高级特性&#xff0c;并通过实战案例提升编程技…

MLM:多模态大型语言模型的简介、微调方法、发展历史及其代表性模型、案例应用之详细攻略

MLM&#xff1a;多模态大型语言模型的简介、微调方法、发展历史及其代表性模型、案例应用之详细攻略 目录 相关文章 AI之MLM&#xff1a;《MM-LLMs: Recent Advances in MultiModal Large Language Models多模态大语言模型的最新进展》翻译与解读 MLM之CLIP&#xff1a;CLIP…

012.Oracle-索引

我 的 个 人 主 页&#xff1a;&#x1f449;&#x1f449; 失心疯的个人主页 &#x1f448;&#x1f448; 入 门 教 程 推 荐 &#xff1a;&#x1f449;&#x1f449; Python零基础入门教程合集 &#x1f448;&#x1f448; 虚 拟 环 境 搭 建 &#xff1a;&#x1f449;&…

LLM 模型压缩之三: FoldGPT

0. 资源链接 论文: FoldGPT: Simple and Effective Large Language Model Compression Scheme 项目: to be released. 1. 背景动机 现有的大语言模型推理存在以下问题&#xff1a; LLM 模型因为有大量的参数&#xff0c;以及 next token 的预测方式&#xff0c;导致 LLM 模…

攻防世界 unseping

unseping 攻防世界web新手练习 -unseping_攻防世界web新手题unseping-CSDN博客 这道题对我来说还是有点难&#xff0c;什么oct绕过命令执行第一次遇到捏&#xff0c;所以基本是跟着别人的wp写的&#xff0c;一点点记录吧 先对源码进行分析 <?php highlight_file(__FILE…

OrangePi AIpro 香橙派 昇腾 Ascend C 算子开发 与 调用 - 通过aclnn调用的方式调用AddCustom算子

OrangePi AIpro 香橙派 昇腾 Ascend C 算子开发 与 调用 通过aclnn调用的方式调用 - AddCustom算子 - 单算子API执行(aclnn) 多种算子调用方式 *开发时间使用场景调用方式运行硬件基于Kernel直调工程&#xff08;快速&#xff09;少单算子调用&#xff0c;快速验证算法逻辑IC…

打造个性化时装购物平台:Spring Boot框架的实践

第1章 绪论 1.1背景及意义 随着社会的快速发展&#xff0c;计算机的影响是全面且深入的。人们生活水平的不断提高&#xff0c;日常生活中人们对时装购物系统方面的要求也在不断提高&#xff0c;喜欢购物的人数更是不断增加&#xff0c;使得时装购物系统的开发成为必需而且紧迫的…

面试必备:接口自动化测试精选面试干货

一、 请问你是如何做接口测试的&#xff1f; 大体来说&#xff0c;经历以下过程&#xff1a;接口需求调研、接口测试工具选择、接口测试用例编写、接口测试执行、接口测试回归、接口测试自动化持续集成。具体来说&#xff0c;接口测试流程分成以下九步&#xff1a; 第一步&am…

2024年,女生到底适合转行ui设计还是软件测试?

作为2024年的就业选择来说&#xff0c;软件测试和UI设计发展都挺不错的 选择这两个方向转行的女生很多。但具体选择测试还是UI设计&#xff0c;最好还是根据你个人的兴趣爱好以及长期的发展路径去选择 比如&#xff1a;薪资、工作稳定性、后续晋升空间、学习难度等等方面~ 如…