Java多线程基础概述

news2024/11/18 13:30:27

 简述多线程:

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

正式着手代码前,需要先理清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.coloradmin.cn/o/496329.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

ChatGPT带你领略自动驾驶技术

一、自动驾驶技术现概述 自动驾驶技术是指利用计算机、传感器和其他设备&#xff0c;使车辆能够在不需要人类干预的情况下自主行驶的技术。目前&#xff0c;自动驾驶技术已经在一些汽车厂商和科技公司中得到广泛应用&#xff0c;但仍然存在一些技术和法律上的挑战&#xff0c;需…

c++类友元函数理解(图、文、代码)

序&#xff1a; 1、初学c&#xff0c;理解阶段&#xff0c;一下为个人理解和案例&#xff0c;陆续更新 一、友元函数和普通函数区别 类的友元函数是函数&#xff0c;但是他可以调用类的私有变量&#xff0c;以下代码&#xff0c;Fun2是报错的&#xff0c;因为这个函数跟A没任…

基于SSM框架流浪动物救助及领养管理系统(spring+springmvc+mybatis+jsp+jquery+layui)

一、项目简介 本项目是一套基于SSM框架流浪动物救助及领养管理系统&#xff0c;主要针对计算机相关专业的正在做毕设的学生与需要项目实战练习的Java学习者。 包含&#xff1a;项目源码、数据库脚本等&#xff0c;该项目附带全部源码可作为毕设使用。 项目都经过严格调试&…

java错题总结(28-30页)

------------------------------------------------------------------------------------------- ------------------------------------------------------------------------------------------- 不考虑类加载&#xff0c; --------------------------------------------…

实验三 磁盘调度算法设计

实验三 磁盘调度算法设计 实验目的&#xff1a; 通过对磁盘调度算法的设计&#xff0c;深入理解提高磁盘访问速度的原理。 实验内容&#xff1a; 模拟实现磁盘调度算法&#xff1a;最短寻道时间优先&#xff08;SSTF&#xff09;和扫描&#xff08;SCAN&#xff09;算法。 …

数字化转型导师坚鹏:企业数字化转型培训如何高效推进评价与改进

企业数字化转型培训如何高效推进、评价与改进 ——以推动企业数字化转型战略落地为核心&#xff0c;实现知行果合一 课程背景&#xff1a; 很多企业都在开展企业数字化转型培训工作&#xff0c;目前存在以下问题急需解决&#xff1a; 不清楚如何有效推进企业数字化转型培训…

JavaScript 箭头函数

&#xff08;许多人所谓的成熟&#xff0c;不过是被习俗磨去了棱角&#xff0c;变得世故而实际了。那不是成熟&#xff0c;而是精神的早衰和个性的消亡。真正的成熟&#xff0c;应当是独特个性的形成&#xff0c;真实自我的发现&#xff0c;精神上的结果和丰收。——周国平&…

Vue|内置指令

Vue的内置指令是带有v-前缀的特殊属性&#xff0c;它的作用是当表达式的值改变时将其一系列连带影响&#xff0c;响应地作用于DOM元素。 推荐专栏&#xff1a;微信小程序实战开发专栏 内置指令v-text指令v-html指令v-cloak指令v-once指令v-pre指令 内置指令 开始前的准备&#…

虹科方案 | CEMEX 使用HK-Edgility 智能边缘计算平台简化其企业 WAN 管理和运营

一、应对价值 130 亿美元的跨国企业的网络挑战 “我们选择 Edgility 是因为其卓越的管理和协调功能&#xff0c;它为我们提供了一个端到端的工具集&#xff0c;可以经济高效地部署和管理我们边缘设备的生命周期。” —— Fernando Garcia -Villaraco Casero, CEMEX 全球IT 战略…

玩家必看!这三款简单好玩的免费小游戏平台!

在这个飞速发展和高度竞争的时代&#xff0c;人们的日程表已经被各种事情排满。时间的紧迫使得我们不可能一刻都停歇下来&#xff0c;正因为如此&#xff0c;我们才需要适当地放慢自己的步伐&#xff0c;寻找些许休闲娱乐的机会来放松自己。 现如今&#xff0c;越来越多的小游…

VSAN 6.7虚拟机精简置备盘空间不回收

原创作者&#xff1a;运维工程师 谢晋 VSAN 6.7虚拟机精简置备盘空间不回收 前提 前提 客户VSAN6.7环境&#xff0c;做了Horzion7.4&#xff0c;近期发现虚拟机存储空间越来越大&#xff0c;远远超过了已分配的磁盘空间大小&#xff0c;发现是数据写入系统内即使删除了数据…

25个著名的WordPress网站案例

想创建免费网站吗&#xff1f;从易服客建站平台开始 500M免费空间&#xff0c;可升级为20GB电子商务网站 创建免费网站 WordPress 内容管理系统为全球35%的网站提供支持。鉴于目前有 17 亿个站点&#xff0c;并且还在增加&#xff0c;您可以算出每秒向网站访问者提供内容的W…

【C++】map和set的介绍+使用

前言&#xff1a; 我们前面一起学习了二叉搜索树&#xff0c;这便是为了引入本章我们所学的map和set容器。map和set的底层实现就和二叉搜索树有关... 目录 &#xff08;一&#xff09;键值对的引入 &#xff08;1&#xff09;关联式容器 &#xff08;2&#xff09;键值对 …

NVIDIA GPU Driver, CUDA 和 PyTorch的版本关系

我们在进行深度学习环境配置的时候&#xff0c;会遇到各种各样的问题。有各种各样的驱动、包需要安装。不同驱动和包之间的还存在版本适配问题&#xff0c;刚入手的同学会一脸懵逼。配置环境成了入门的第一道门槛。我现在总结了NVIDIA 显卡 Anaconda资源库 PyTorch深度学习框…

Packet Tracer - 配置扩展 ACL - 场景 2

Packet Tracer - 配置扩展 ACL - 场景 2 拓扑图 地址分配表 设备 接口 IP 地址 子网掩码 默认网关 RTA G0/0 10.101.117.49 255.255.255.248 不适用 G0/1 10.101.117.33 255.255.255.240 不适用 G0/2 10.101.117.1 255.255.255.224 不适用 PCA NIC 10.101…

No primary or single public constructor found for interface java.util.List

我的爆错原因是途中ids忘记标注注解PathVariable了&#xff0c;因为要传入一系列的整数的列表对象到路径/emps/deleteEmps/{ids}中&#xff0c;所以我这里就是加上注解PathVariable就OK了。

TAPD使用规范

目录 https://www.bilibili.com/?spm_id_from333.788.0.0我该如何理解这段网址&#xff1f; ?spm_id_from333.788.0.0&#xff1a;表示查询字符串&#xff0c;用于向服务器传递额外的参数信息。在这个例子中&#xff0c;该查询字符串可能用于追踪网站访问来源或统计数据分析…

windows权限维持之计划任务

schtasks常用参数: /Create 创建新计划任务。 /Delete 删除计划任务。 /Query 显示所有计划任务。 /Change 更改计划任务属性。 /Run 按需运行计划任务。 /End 中止当前正在运行的计划任务。 …

会声会影2023旗舰版升级更新及新增功能讲解

会声会影2023新版增加了众多新功能&#xff0c;在海外同样引起了很多关注。每年会声会影更新他都会制作一条&#xff0c;今年的你也不容错过&#xff0c;相信你看完对2023版会声会影将更有兴趣升级~ 建议2022版之前的老朋友&#xff0c;一定要升级2023版&#xff0c;在程序的性…

Linux的字符集及编码

Linux 字符集设置 1、查询 (1) 查看当前服务器字符集: # locale (2) 查看服务器支持的字符集: # locale -a 2、修改linux系统字符集的方式有如下两种&#xff1a; (1) 直接设置变量的方式修改&#xff0c;命令如下两条命令&#xff1a; [root~]# LANG"xxx" 或者…