Java多线程基础概述

 简述多线程:

是指从软件或者硬件上实现多个线程并发执行的技术。
具有多线程能力的计算机因有硬件支持而能够在同一时间执行多个线程,提升性能。

正式着手代码前,需要先理清4个概念:并发,并行,进程,线程

1.并发:

可以理解为在同一时刻,有多个指令在单核CPU上交替执行。单核cpu就好比一条赛道,赛车就好比指令,第一名才有资格使用cpu的资源,所以在单核cpu中指令的并发就像赛车的缠斗,互相抢夺第一名来使用cpu的资源。

 2.并行:

在同一时刻,有多个指令在多个CPU上同时执行。并行的前提是cpu资源充足,在多核cpu中可以实现并行,就像车道来到了多车道宽敞的公路,不比再过分争夺资源,可以同时悠闲的进行多个指令。

总结一下:

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

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

3.进程:

是正在运行的软件。手机中的APP,电脑中的C/S系统,都可以看做一个进程,用上面的例子中的标注,一台摩托车就可以看做一个进程。规范些的描述如下:

独立性: 进程是一个能独立运行的基本单位,同时也是系统分配资源和调度的独立单位。

动态性: 进程的实质是程序的一次执行过程,进程是动态产生,动态消亡的。

并发性:任何进程都可以同其他进程一起并发执行。

4.线程:

是进程中的单个顺序控制流,是一条执行路径。就好比手机App中的一条下单或者退款指令,或者电脑中的一次点击提交操作。相对进程是部分性的存在,在进程中独立运行,用上述例子就好比摩托车中的发动机活塞往复。

 言归正传,下面列举下多线程的实现方式:

继承Thread类的方式进行实现

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

利用Callable和Future接口方式实现


 1.继承Thread类方式的基础实现:

package com.demo;

public class ThreadDemo extends Thread{

    @Override
    public void run(){
        super.run();
        System.out.println("第一段线程");
    }

    public static void main(String[] args) {
        ThreadDemo threadDemo = new ThreadDemo();
        threadDemo.start();
    }
}


样例继承Thread类后重写run()方法,然后再main方法中new一个样例出来,通过执行Thread类自带的start()方法运行后即可实现程序的运行。执行结果可见:
在这里插入图片描述

这里注意两个地方:

(1)为什么要重写run()方法?

因为run()是用来封装被线程执行的代码,也就是我们在使用时需要将业务逻辑写在run方法中。

(2)run()方法和start()方法的区别?

run():封装线程执行的代码,直接调用,相当于普通方法的调用,并没有开启线程。

start():启动线程;然后由JVM调用此线程的run()方法


 2.实现Runnable接口方式:

package com.demo;

public class RunableDemo implements Runnable {

    @Override
    public void run(){
        System.out.println("RUNABLE线程");
    }

    public static void main(String[] args) {
        //runable是个接口,没有start方法去执行,需要借助thread类
        RunableDemo runableDemo = new RunableDemo();
        Thread thread = new Thread(runableDemo);
        thread.start();
    }
}


这里的Runnable接口不是一个类,没有自带的start()方式,所以在执行时需要借助Thread类,将样例以参数的形式传入来实现执行,thread类自带的tread方法如下:
参数定义了Runnable类型的入参
该方式运行结果:

在这里插入图片描述
3.实现Callable接口方式:

public static class MyCallable implements Callable<String> {
        @Override
        public String call() throws Exception {
            for (int i = 0; i < 10; i++) {
                System.out.println("超车" + i);
            }
            //返回值就表示线程运行完毕之后的结果
            return "获胜";
        }
    }

public static void main(String[] args) throws ExecutionException, InterruptedException {

            //线程开启之后需要执行里面的call方法
            MyCallable mc = new MyCallable();

            //Thread t1 = new Thread(mc);

            //可以获取线程执行完毕之后的结果.也可以作为参数传递给Thread对象
            FutureTask<String> ft = new FutureTask<>(mc);

            //创建线程对象
            Thread t1 = new Thread(ft);


            //开启线程
            t1.start();
            String s = ft.get();
            System.out.println(s);
        }

    

因为thread中只能传入rannable接口,所以callable接口在实现时需要借助FutureTask类,该类的类关系图中可知,最终是继承了rannable,所以可以在thread中使用。

 具体实现步骤如下:

 定义一个类MyCallable实现Callable接口

在MyCallable类中重写call()方法,这里call()方法就是替代上面的run()方法,callable没有run()方法

创建MyCallable类的对象

创建Future的实现类FutureTask对象,把MyCallable对象作为构造方法的参数

创建Thread类的对象,把FutureTask对象作为构造方法的参数,即call()方法返回的数据, 启动线程 再调用get方法,就可以获取线程结束之后的结果。


执行程序结果如下:

 三种方式对比:


以上就是3种基本的多线程最简单的实现方式,简单到底了。

除了最简单的实现方式外,还有一些关于线程的参数设置规则:

1.设置,获取线程的名字,线程休眠

代码演示如下,这里用threa类为例:

 public static class ThreadDemo extends Thread {
//        public ThreadDemo(String name) {
//            super(name);
//        }
        @Override
        public void run() {
            super.run();
            for (int i = 0; i < 15; i++) {
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

                System.out.println(getName() + "超车---积分" + i);
            }
        }

        public static void main(String[] args) throws ExecutionException, InterruptedException {
//            ThreadDemo yamaha = new ThreadDemo("雅马哈");
//            ThreadDemo honda = new ThreadDemo("本田");
              ThreadDemo yamaha = new ThreadDemo();
              ThreadDemo honda = new ThreadDemo();
            yamaha.setName("雅马哈");
            honda.setName("本田");
            yamaha.start();
            honda.start();
}

上述例子中有ThreadDemo构造方法时,可以在new实例的时候直接命名,没有构造方法,可以通过setName()方法给线程命名。

String getName​():返回此线程的名称,需要注意的是,getName()方法只有thread类才可以直接用来获取线程名称,其余两种需要使用Thread.currentThread().getName()来获取当前线程名称。

public static void sleep(long time):让线程休眠指定的时间,单位为毫秒。

例子中我创建了2个线程分别命名为雅马哈和本田

运行结果:

 线程调度       

 多线程的并发运行时,计算机中的CPU,在任意时刻只能执行一条机器指令。每个线程只有获得CPU的使用权才能执行代码。各个线程轮流获得CPU的使用权,分别执行各自的任务。

线程有两种调度模型

1.分时调度模型:所有线程轮流使用 CPU 的使用权,平均分配每个线程占用 CPU 的时间片

2.抢占式调度模型:优先让优先级高的线程使用 CPU,如果线程的优先级相同,那么会随机选择一个,优先级高的线程获取的 CPU 时间片相对多一些

Java使用的是抢占式调度模型:

线程的优先级

public final void setPriority(int newPriority)    设置线程的优先级

public final int getPriority()        获取线程的优先级

这里上代码示例演示下,用Callable的方式实现:

首先定义一个mycallable类:

public static class MyCallable implements Callable<String> {
        @Override
        public String call() throws Exception {
            for (int i = 0; i < 10; i++) {
                System.out.println(Thread.currentThread().getName() + "超车" + i);
            }
            //返回值就表示线程运行完毕之后的结果
            return Thread.currentThread().getName() +"完赛";
        }
    }

然后是实现方式:

 public static void main(String[] args) throws ExecutionException, InterruptedException {

            //优先级: 1 - 10 默认值:5
            MyCallable mc = new MyCallable();

            FutureTask<String> ducati = new FutureTask<>(mc);

            Thread t1 = new Thread(ducati);
            t1.setName("马力狗");
            t1.setPriority(10);
            System.out.println("马力狗马力优先级"+t1.getPriority());
            t1.start();
            System.out.println(ducati.get());

            MyCallable mc2 = new MyCallable();

            FutureTask<String> suzuki = new FutureTask<>(mc2);

            Thread t2 = new Thread(suzuki);
            t2.setName("GSX");
            t2.setPriority(1);
            System.out.println("GSX马力优先级"+t2.getPriority());
            t2.start();
            System.out.println(suzuki.get());
}

在优先级设置方法setPriority的源码中可以看到有两个参数控制最大最小优先级,最大是10,最小是1。在设置时不要超过这个范围,否则会引发报错

 由于电脑配置较高,而且执行的逻辑较简单,数据量小,所以运行结果体现不出cpu资源的竞争,看上去都是按着代码顺序执行的,就不展示结果了。

 以上内容如有不对欢迎指正,业余时间总结记录一下。

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

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

相关文章

TinyJAMBU的制动原理——一种轻量化的认证密码

关于TinyJAMBU的定义和介绍在另一篇博文已经介绍过了&#xff0c;这里只对其动作原理进行描述和说明。 对应的博文链接如下&#xff1a;TinyJAMBU&#xff1a;一种轻量化密码介绍 首先&#xff0c;该密码是一个流密码体系的块密码框架。其加密模式整体上来看是块密码&#xff0…

让语言学习更简单的 WordFlow

作为一个英语并不是那么特别好的计算机专业学生&#xff0c;长期积累英语的学习对个人发展还是有意义的。简单来说&#xff0c;我在语言上最大的两个问题&#xff0c;一个自己「不理解」&#xff0c;另一个是自己「不会表达」。 上述两个问题主要体现在口语层面&#xff0c;而…

实验二 存储器管理

实验二 存储器管理 实验目的&#xff1a; 理解各类置换算法的原理和虚拟存储器管理的方法。 实验内容&#xff1a; 编程实现LRU算法或CLOCK/改进算法等置换算法&#xff08;二选一&#xff09;&#xff0c;模拟实现虚拟存储器的地址变换过程。 实验步骤&#xff1a; 1…

【Golang项目实战】用Go写一个学生信息管理系统,真的太酷啦| 保姆级详解,附源码——建议收藏

博主简介&#xff1a;努力学习的大一在校计算机专业学生&#xff0c;热爱学习和创作。目前在学习和分享&#xff1a;数据结构、Go&#xff0c;Java等相关知识。博主主页&#xff1a; 是瑶瑶子啦所属专栏: Go语言核心编程近期目标&#xff1a;写好专栏的每一篇文章 学习了Go的基…

图神经网络:在自定义数据集上动手实现图神经网络

文章说明&#xff1a; 1)参考资料&#xff1a;PYG官方文档。超链。 2)博主水平不高&#xff0c;如有错误还望批评指正。 文章目录 自定义数据集动手实现图神经网络自定义数据集训验测集拆分&#xff0c;创建Data的数据结构&#xff0c;观察Data的基本信息&#xff0c;可视化图网…

震惊,为了学会泛型类竟做这种事?!

上一节&#xff0c;我们基本学会了Java泛型类的用法。 传送门&#xff1a;彻底弄懂Java的泛型 - 泛型类 这一节&#xff0c;我们转变一下风格&#xff0c;具体是什么风格呢&#xff0c;你马上就懂了。 宝子们&#xff0c;欢迎大家来到我们的泛型直播间&#xff0c;这一讲呢&a…

Ansible的脚本-playbook 剧本

目录 1.剧本&#xff08;playbook&#xff09; 1.playbook介绍 2. playbooks 的组成 3.案例&#xff1a;编写httpd的playbook 4.定义、引用变量 5.指定远程主机sudo切换用户 6.when条件判断 7.迭代 2.playbook的模块 1.Templates 模块 2.tags 模块 3.Roles 模块 1.…

【Linux从入门到精通】vim的基本使用各种操作详解

文章目录 一、vim编辑器简单介绍 二、vim编辑器的四种模式 2、1 正常/普通/命令模式(Normal mode) 2、2 插入模式(Insert mode) 2、3 末行模式(last line mode) 三、命令模式的相关操作实例 3、1 光标的相关操作 3、2 文本操作 四、插入模式下的相关操作 五、末行模式下的相关操…

Java—JDK8新特性—函数式接口

目录 函数式接口 3.1 什么是函数式接口 3.2 functionalinterface注解 源码分析 3.3 Lambda表达式和函数式接口关系 3.4 使用函数式接口 函数式接口 3.1 什么是函数式接口 如果一个接口中只包含一个抽象方法&#xff0c;这个接口称为函数式接口 如果一个接口包含&#xff0…

K8S管理系统项目实战[API开发]-2

后端: gogin 后端代码地址GitHub - yunixiangfeng/k8s-platform: K8s管理系统后端: gogin 5、存储与配置 5.1 ConfigMap 5.2 Secret 5.3 PersistentVolumeClaims 6、工作流 6.1 流程设计 6.2 数据库操作&#xff08;GORM&#xff09; &#xff08;1&#xff09;初始化…

交换机-Exchanges

交换机 Exchanges 概念 RabbitMQ 消息传递模型的核心思想是: 生产者生产的消息从不会直接发送到队列。实际上&#xff0c;通常生产者甚至都不知道这些消息传递传递到了哪些队列中。相反&#xff0c;生产者只能将消息发送到交换机(exchange)&#xff0c;交换机工作的内容非常简…

正则表达式-基本元字符和语法规则

© Ptw-cwl 文章目录 字符匹配元字符.元字符[]元字符[^]元字符*元字符元字符?元字符{}元字符|元字符()元字符^元字符$元字符\元字符\d元字符\w元字符\s元字符\b元字符\B元字符*?、?、??、{n,m}?元字符(?)、(?!)元字符(?:)元字符\1、\2等元字符^、$元字符&#x…

JavaSE基础(二)—— 类型转换、运算符、键盘录入

目录 一、类型转换 1. 自动类型转换 1.1 自动类型转换的底层原理&#xff1a; ​1.2 自动类型转换的其他形式​编辑 2. 表达式的自动类型转换 3. 强制类型转换 3.1 强制类型转换底层原理​编辑 3.2 注意事项 二、运算符 1. 算数运算符 1.1 案例&#xff1a;数值拆分…

PCA主成成分分析例题详解

主成分分析是一种降维算法&#xff0c;它能将多个指标转换为少数几个主成分&#xff0c;这些主成分是原始变量的线性组合&#xff0c;且彼此之间互不相关&#xff0c;其能反映出原始数据的大部分信息 需要了解具体细节可看此视频&#x1f449;&#xff1a;什么是主成成分分析PC…

Linux安装MongoDB数据库,并内网穿透远程连接

文章目录 前言1. 配置Mongodb源2. 安装MongoDB3. 局域网连接测试4. 安装cpolar内网穿透5. 配置公网访问地址6. 公网远程连接7. 固定连接公网地址8. 使用固定地址连接 转载自Cpolar Lisa文章&#xff1a;Linux服务器安装部署MongoDB数据库 - 无公网IP远程连接「内网穿透」 前言 …

SpringBoot访问静态资源

SpringBoot项目中没有WebApp目录&#xff0c;只有src目录。在src/main/resources下面有static和templates两个文件夹。SpringBoot默认在static目录中存放静态资源&#xff0c;而templates中放动态页面。 static目录 SpringBoot通过/resources/static目录访问静态资源&#xff…

完成A轮融资,倍思如何发力场景化为品牌创造广阔未来?

凛冬过后的消费电子正在重新凝聚资本的目光。 近日&#xff0c;深圳市倍思科技有限公司宣布完成由深创投、中金资本联合领投&#xff0c;越秀产业基金、高榕资本跟投&#xff0c;金额数亿元人民币的A轮融资。 分析人士指出&#xff0c;消费电子的行业景气度在逐渐恢复&#x…

中国社科院与美国杜兰大学金融管理硕士项目——迎接立夏,切莫忘记自我成长

五月的风吹走了春季&#xff0c;今天我们迎来立夏。作为夏季的第一个节气&#xff0c;立夏常被人们当做万物蓄满能量&#xff0c;即将加速生长的标志。而在职的我们&#xff0c;也应该跟这世间万物一样&#xff0c;在季节交替之时沉淀自己、努力向上成长。在社科院与杜兰大学金…

“人工智能教父”从谷歌离职 称后悔发展AI,为世人敲响警钟?

在加入谷歌的第十年、深度学习迎来爆发式发展的当下&#xff0c;被誉为“人工智能教父”的Geoffrey Hinton已从谷歌离职&#xff0c;只是为了告诫人们AI已经变得很危险。 公开资料显示&#xff0c;Geoffrey Hinton在2013年加入谷歌&#xff0c;曾任副总裁&#xff0c;研究机器学…

成为数据分析师,需要具备哪些技能?

随着互联网的发展&#xff0c;数据分析师的特点越来越明显&#xff0c;对数据分析师综合素质的要求也较高。 1、较强的数据挖掘、信息整理、和逻辑分析能力 数据分析&#xff0c;也是数据分析师的一个方向。 制作日常性的经营报表&#xff0c;对公司或者行业KPI指标进行拆解…