【Java】详解多线程的概述及三种创建方法

🌺个人主页Dawn黎明开始

🎀系列专栏Java
每日一句身在井隅,心向阳光,眼里有诗,自在远方

📢欢迎大家:关注🔍+点赞👍+评论📝+收藏⭐️


文章目录

一.🔐多线程

📋前言

1.1🔓进程

1.1.1🔑什么是进程?

1.1.2🔑多进程有什么意义呢?

1.2🔓线程

1.2.1🔑什么是线程呢?

1.2.2🔑多线程有什么意义呢?

1.3🔓Java程序的运行原理

1.3.1🔑原理

1.3.2🔑思考题

二.🔐多线程的三种创建方法

2.1.🔓继承Thread类

2.1.1🔑步骤

2.1.2🔑实例练习

2.1.3🔑思考

2.2🔓实现Runnable接口

2.2.1🔑步骤

2.2.2🔑实例练习

2.3🔓实现Callable接口

2.2.1🔑步骤

2.2.2🔑实例练习

2.4🔓三种方法的对比分析

2.4.1🔑分析

2.4.2🔑优点

2.4.3🔑说明


一.🔐多线程

📋前言

          要想了解多线程,必须先了解线程,而要想了解线程,必须先了解进程,因为线程是依赖于进程而存在。

1.1🔓进程

1.1.1🔑什么是进程?

定义

        在一个操作系统中,每个独立执行的程序都可称之为一个进程,也就是“正在运行的程序”。

        进程是系统进行资源分配和调用的独立单位。每一个进程都有它自己的内存空间和系统资源。

        通过任务管理器我们就看到了进程的存在,而通过观察,我们发现只有运行的程序才会出现进程。

1.1.2🔑多进程有什么意义呢?

       单进程的计算机只能做一件事情,而我们现在的计算机都可以做多件事情。

       举例:一边玩游戏(游戏进程),一边听音乐(音乐进程)。

 📝总结 

现在的计算机都是支持多进程的,可以在一个时间段内执行多个任务。多进程可以提高CPU的使用率(前提:单CPU系统)。

🔥思考

            一边玩游戏,一边听音乐是同时进行的吗?

            不是。因为单CPU在某一个时间点上只能做一件事情,在计算机中,所有的应用程序都是由CPU执行的,对于一个CPU而言,在某个时间点只能运行一个程序,也就是说只能执行一个进程,操作系统会为每一个进程分配一段有限的CPU使用时间,CPU在这段时间中执行某个进程,然后会在下一段时间切换到另一个进程中去执行。 

            而我们在玩游戏,或者听音乐的时候,是CPU在做着程序间的高效切换让我们觉得是同时进行的。

 📝总结       

     (1).在多任务操作系统中,表面上看是支持进程并发执行的,例如可以一边听音乐一边聊天,但实际上这些进程并不是在同一时刻运行的。

    (2).由于CPU运行速度非常快,能在极短的时间内在不同的进程之间进行切换,所以给人以同时执行多个程序的感觉。

1.2🔓线程

1.2.1🔑什么是线程呢?

定义

        多任务操作系统中,每个运行的程序都是一个进程,用来执行不同的任务,而在一个进程中还可以有多个执行单元同时运行,来同时完成一个或多个程序任务,这些执行单元可以看做程序执行的一条条线索,被称为线程。

说明

(1).单线程都是按照调用顺序依次往下执行,没有出现多段程序代码交替运行的效果,而多线程程序在运行时,每个线程之间都是独立的,它们可以并发执行

(2).多线程可以充分利用CUP资源,进一步提升程序执行效率

(3).多线程看似是同时并发执行的,其实不然,它们和进程一样,也是由CPU控制并轮流执行的,只不过CPU运行速度非常快,故而给人同时执行的感觉。 

多线程举例

    扫雷程序、百度云盘、百度网盘下载

1.2.2🔑多线程有什么意义呢?

        多线程的存在,不是提高程序的执行速度。其实是为了提高应用程序的使用率。

(1).程序的执行其实都是在抢CPU的资源,CPU的执行权  。

(2).多个进程是在抢这个资源,而其中的某一个进程如果执行路径比较多,就会有更高的几率抢到CPU的执行权。

(3).我们是不能确定哪一个线程能够在哪个时刻抢到,所以线程的执行具有随机性

1.3🔓Java程序的运行原理

1.3.1🔑原理

         Java命令去启动JVM,JVM会启动一个进程,该进程会创建了一个主线程调用main方法。

1.3.2🔑思考题

         🔥JVM的启动是多线程的吗?

      是多线程的,原因是:垃圾回收线程也要先启动,否则很容易会出现内存溢出。现在的垃圾回收线程加上前面的主线程,最低启动了两个线程,所以,jvm的启动其实是多线程的。

二.🔐多线程的三种创建方法

2.1.🔓继承Thread类

如何获取线程对象的名称呢?
 * public final String getName():获取线程的名称。
 * 如何设置线程对象的名称呢?
 * public final void setName(String name):设置线程的名称
 * 
 * 针对不是Thread类的子类中如何获取线程对象名称呢?
 * public static Thread currentThread():返回当前正在执行的线程对象
 * Thread.currentThread().getName()

2.1.1🔑步骤

    A:自定义类MyThread继承Thread类。

    B:MyThread类里面重写run()

    C:创建对象

    D:启动线程

2.1.2🔑实例练习

🚩实例练习1

代码如下👇🏻 

package Process;

public class MyThread extends Thread {
	@Override
	public void run() {  //多线程的任务
		for(int i=0;i<200;i++) {
			System.out.println(i);
		}
	}
}
package Process;

public class MyThreadDemo {

	public static void main(String[] args) {
		// 创建两个线程对象
		MyThread my1 = new MyThread();
		MyThread my2 = new MyThread();
		my1.start();
		my2.start();
	}
}

运行结果👇🏻

 🚩实例练习2

代码如下👇🏻 

package Process;

public class MyThread1 extends Thread {
	
	public MyThread1() {
		super();
	}

	public MyThread1(String name) {
		super(name);
	}

	@Override
	public void run() {  //多线程的任务
		for(int i=0;i<200;i++) {
			System.out.println(this.getName()+":"+i);
		}
	}
}
package Process;

public class MyThreadDemo1 {
	// 创建两个线程对象
	public static void main(String[] args) {
		MyThread1 my3 = new MyThread1("张三");
		MyThread1 my4 = new MyThread1("李四");
		my3.start();
		my4.start();
		for(int i=0;i<20;i++) {
			System.out.println(Thread.currentThread().getName()+":"+i);
		}
	}

}

运行结果👇🏻

2.1.3🔑思考

🔥思考: 调用run()方法为什么是单线程的呢?
         因为run()方法直接调用其实就相当于普通的方法调用,所以你看到的是单线程的效果
         要想看到多线程的效果,就必须用另一个方法:start()

🔥面试题:run()和start()的区别?

     run():仅仅是封装被线程执行的代码,直接调用是普通方法

     start():首先启动了线程,然后再由jvm去调用该线程的run()方法。

2.2🔓实现Runnable接口

2.2.1🔑步骤

   A:自定义类MyRunnable实现Runnable接口

   B:重写run()方法

   C:创建MyRunnable类的对象

   D:创建Thread类的对象,并把C步骤的对象作为构造参数传递

   E:启动线程(Thread类的对象)

🔥思考:有了方法1,为什么还来一个方法2呢?

(1).可以避免由于Java单继承带来的局限性

(2).适合多个相同程序的代码去处理同一个资源的情况,把线程同程序的代码,数据有效分离,较好的体现了面向对象的设计思想。

2.2.2🔑实例练习

代码如下👇🏻 

package Process;

public class MyRunnable implements Runnable {

	@Override
	public void run() {
		for(int i=0;i<200;i++) {
			System.out.println(Thread.currentThread().getName()+":"+i);
		}

	}

}
package Process;

public class MyRunnableDemo {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		MyRunnable mr =new MyRunnable();
		Thread t1 =new Thread(mr,"王五");
		t1.start();
		Thread t2=new Thread(mr,"赵六");
		t2.start();
		
		//简洁方法
//		new Thread(new MyRunnable(),"a").start();
//		new Thread(new MyRunnable(),"b").start();
	}

}

  运行结果👇🏻

 

2.3🔓实现Callable接口

2.2.1🔑步骤

   A:自定义类MyCallable实现Callable接口

   B:重写call()方法

   C:创建MyCallable类的对象

   D:创建FutureTask类的对象,并把C步骤的对象作为构造参数传递

   E:创建Thread类的对象,并把D步骤的对象作为构造参数传递

   F:启动线程(Thread类的对象)

🔥与两种方式的区别:

(1).该方式的线程执行结束返回一个值

(2).可以通过FutureTask类的对象的get()方法接受返回值

2.2.2🔑实例练习

代码如下👇🏻 

package Process;

import java.util.concurrent.Callable;

public class MyCallable implements Callable<Object> {

	@Override
	public Object call() throws Exception {
		int sum=0;
		for(int i=0;i<=10;i++) {
			System.out.println(Thread.currentThread().getName()+":"+i);
			sum+=i;
		}
		return sum;
	}

}
package Process;

import java.util.concurrent.FutureTask;

public class MyCallableDemo {

	public static void main(String[] args) throws Exception {
		//2、创建Callable接口的实现类对象
	  MyCallable MyThread1=new  MyCallable();
	  //3、使用 FutureTask封装Callable接口
	  FutureTask<Object> ft1 =new  FutureTask<>(MyThread1);
	  //4、使用Thread(Runable target,String name)构造方法创建线程对象
	  Thread thread1 =new Thread(ft1,"线程A");
	  //5、调用线程对象得start()方法启动线程
	  thread1.start();
	  //创建并启动另一个线程
	  FutureTask<Object> ft2 =new  FutureTask<>(MyThread1);
	  Thread thread2 =new Thread(ft2,"线程B");
	  thread2.start();
	  //可以通过FutureTask对象得方法管理返回值
	  System.out.println("线程A的返回值:"+ft1.get());
	  System.out.println("线程A的返回值:"+ft2.get());
	}

}

 运行结果👇🏻

2.4🔓三种方法的对比分析

2.4.1🔑分析

(1).继承Thread类是最简单和直接的方式,但限制了类的扩展性。

(2).实现Runnable接口提供了更好的灵活性,使得多个线程可以共享一个任务。

(3).实现Callable接口则更适合需要获取线程执行结果的情况,可以更方便地处理线程执行后的返回值或异常。

2.4.2🔑优点

通过实现Runnable接口(或者Callable接口)相对于继承Thread类实现多线程来说

(1).适合多个线程去处理同一个共享资源的情况。

(2).可以避免Java单继承带来的局限性。

2.4.3🔑说明

事实上,实际开发中大部分的多线程应用都会采用Runnable接口或者Callable接口的方式实现多线程。


🌺建议大家亲自动手操作,学编程,多实践练习是提升编程技能的必经之路。

🌺欢迎大家在评论区进行讨论和指正!

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

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

相关文章

【JVM系列】- 寻觅·方法区的内容

寻觅方法区的内容 &#x1f604;生命不息&#xff0c;写作不止 &#x1f525; 继续踏上学习之路&#xff0c;学之分享笔记 &#x1f44a; 总有一天我也能像各位大佬一样 &#x1f31d;分享学习心得&#xff0c;欢迎指正&#xff0c;大家一起学习成长&#xff01; 文章目录 寻觅…

Pyside6/PYQT6如何实现无边框设计,解决无边框窗口无法移动的问题

文章目录 💢 问题 💢💯 解决方案 💯🍔 准备工作🐾 操作步骤🐾 窗口无边框🐾 窗口透明🐾 实现窗口可移动⚓️ 相关链接 ⚓️💢 问题 💢 有时候我们需要一个无边框的UI设计来实现/美化一些功能,如:制作一个桌面时钟,进度条展示等,要实现无边框其实很简…

【 第十一章】软件设计师 之 面向对象设计与结构化分析设计

文章底部有个人公众号&#xff1a;热爱技术的小郑。主要分享开发知识、学习资料、毕业设计指导等。有兴趣的可以关注一下。为何分享&#xff1f; 踩过的坑没必要让别人在再踩&#xff0c;自己复盘也能加深记忆。利己利人、所谓双赢。 备考资料导航 软考好处&#xff1a;软考的…

【Proteus仿真】【Arduino单片机】LCD1602液晶显示

文章目录 一、功能简介二、软件设计三、实验现象联系作者 一、功能简介 本项目使用Proteus8仿真Arduino单片机控制器&#xff0c;使用LCD1602液晶等。 主要功能&#xff1a; 系统运行后&#xff0c;LCD1602液晶显示各种效果。 二、软件设计 /* 作者&#xff1a;嗨小易&#x…

MSF图形化工具Viper快速安装

简介 Viper(炫彩蛇)是一款图形化内网渗透工具,将内网渗透过程中常用的战术及技术进行模块化及武器化. Viper(炫彩蛇)集成杀软绕过,内网隧道,文件管理,命令行等基础功能. Viper(炫彩蛇)当前已集成70个模块,覆盖初始访问/持久化/权限提升/防御绕过/凭证访问/信息收集/横向移动等…

【MySQL系列】 第二章 · SQL(下)

写在前面 Hello大家好&#xff0c; 我是【麟-小白】&#xff0c;一位软件工程专业的学生&#xff0c;喜好计算机知识。希望大家能够一起学习进步呀&#xff01;本人是一名在读大学生&#xff0c;专业水平有限&#xff0c;如发现错误或不足之处&#xff0c;请多多指正&#xff0…

保姆级jupyter lab配置清单

❤️觉得内容不错的话&#xff0c;欢迎点赞收藏加关注&#x1f60a;&#x1f60a;&#x1f60a;&#xff0c;后续会继续输入更多优质内容❤️ &#x1f449;有问题欢迎大家加关注私戳或者评论&#xff08;包括但不限于NLP算法相关&#xff0c;linux学习相关&#xff0c;读研读博…

通过SD卡给某摄像头植入可控程序

0x01. 摄像头卡刷初体验 最近研究了手上一台摄像头的sd卡刷机功能&#xff0c;该摄像头只支持fat32格式的sd卡&#xff0c;所以需要先把sd卡格式化为fat32&#xff0c;另外微软把fat32限制了最大容量32G&#xff0c;所以也只能用不大于32G的sd卡来刷机。 这里使用32G的sd卡来…

09 # 手写 some 方法

some 使用 some() 方法测试数组中是否至少有一个元素通过了由提供的函数实现的测试。如果在数组中找到一个元素使得提供的函数返回 true&#xff0c;则返回 true&#xff1b;否则返回 false。它不会修改数组。 ele&#xff1a;表示数组中的每一个元素index&#xff1a;表示数…

Unity中Shader的雾效

文章目录 前言一、Unity中的雾效在哪开启二、Unity中不同种类雾的区别1、线性雾2、指数雾1&#xff08;推荐用这个&#xff0c;兼具效果和性能&#xff09;3、指数雾2&#xff08;效果更真实&#xff0c;性能消耗多&#xff09; 三、在我们自己的Shader中实现判断&#xff0c;是…

vue设计原理-带你重走vue诞生路程

我们首先看下面这个小demo demo源码: <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><meta http-equiv"X-UA-Compatible" content"IEedge"><meta name"viewport" conten…

RT-DETR 应用 CARAFE:特征内容感知重新组装

特征上采样是现代卷积神经网络架构中的关键操作,例如特征金字塔。其设计对于密集预测任务,如目标检测和语义/实例分割至关重要。在本研究中,我们提出了一种称为内容感知特征重组(CARAFE)的通用、轻量级且高效的操作符,以实现这一目标。CARAFE具有以下几个优点:(1)大的…

算法通关村第七关-青铜挑战二叉树的深度优先遍历(递归)

二叉树的深度优先遍历 今天我们来说二叉树的深度优先遍历 , 这次用简单但有点难理解的方式递归来实现 , 对应LeetCode 144,145 二叉树的前序遍历 描述 : 给你二叉树的根节点 root &#xff0c;返回它节点值的 前序 遍历。 题目 : LeetCode 二叉树的前序遍历 : 144. 二叉…

【FAQ】Gradle开发问题汇总

1. buildSrc依赖Spring Denpendency时报错 来自预编译脚本的插件请求不能包含版本号。请从有问题的请求中删除该版本&#xff0c;并确保包含所请求插件io.spring.dependency-management的模块是一个实现依赖项 解决方案 https://www.5axxw.com/questions/content/uqw0grhttps:/…

K8S知识点(九)

&#xff08;1&#xff09;Pod详解-结构和定义 一级属性有下面这些&#xff1a;前两个属性是字符串&#xff0c;上面有定义 kind&#xff1a;Pod version&#xff1a;v1 下面的属性是object 还可以继续查看子属性&#xff1a;二级属性 还可以继续查看三级属性&#xff1a; 通…

微软近日限制员工访问ChatGPT!

作者 | 撒鸿宇 据CNBC报道&#xff0c;在这周四的短时间内&#xff0c;微软的员工被禁止使用ChatGPT。 微软在其内部网站的更新中表示&#xff1a;“由于安全和数据问题&#xff0c;一些AI工具不再对员工开放。”据CNBC查证&#xff0c;他们看到了一张截图&#xff0c;该截图显…

KubeSphere 社区双周报 | KubeSphere 3.4.1 发布 | 2023.10.27-11.09

KubeSphere 社区双周报主要整理展示新增的贡献者名单和证书、新增的讲师证书以及两周内提交过 commit 的贡献者&#xff0c;并对近期重要的 PR 进行解析&#xff0c;同时还包含了线上/线下活动和布道推广等一系列社区动态。 本次双周报涵盖时间为&#xff1a;2023.10.27-2023.…

Nuxt.js——基于 Vue 的服务端渲染应用框架

文章目录 前言一、知识普及什么是服务端渲染什么是客户端渲染&#xff1f;服务端渲染与客户端渲染那个更优秀&#xff1f; 二、Nuxt.js的特点Nuxt.js的适用情况&#xff1f; 三、Vue是如何实现服务端渲染的&#xff1f;安装依赖使用vue安装 Nuxt使用npm install安装依赖包使用n…

UE特效案例 —— 角色刀光

目录 一&#xff0c;环境配置 二&#xff0c;场景及相机设置 三&#xff0c;效果制作 刀光制作 地裂制作 击打地面炸开制作 一&#xff0c;环境配置 创建默认地形Landscape&#xff0c;如给地形上材质需确定比例&#xff1b;添加环境主光源DirectionalLight&#xff0c;设…

3.如何实现 API 全局异常处理?-web组件篇

文章目录 1. 统一异常处理 1. 统一异常处理 在 Spring MVC 中&#xff0c;通过 ControllerAdvice ExceptionHandler 注解&#xff0c;声明将指定类型的异常&#xff0c;转换成对应的 CommonResult 响应。实现的代码&#xff0c;可见 GlobalExceptionHandler类。