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

news2024/11/17 13:18:22

🌺个人主页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接口的方式实现多线程。


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

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

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

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

相关文章

typora保护机制与注册逆向分析

、起因 一直比较喜欢Typora的简洁与美观&#xff08;尝试过用 vscode 搭配插件编辑 markdown 文件&#xff0c;体验还是要差一些的&#xff09;&#xff0c;突然发现自己windows机器上很久前安装的typora不让用了&#xff0c;提示&#xff1a; 幸好原始安装文件还在&#xf…

基于 Gin 的 HTTP 代理 demo

上次用 TCP 模拟了一个 HTTP 代理之后&#xff0c;感觉那样还是太简陋了&#xff0c;想着是不是可以用框架来做一个有点实际用处的东西。所以&#xff0c;就思索如何用 golang 的 Gin 框架来实现一个&#xff1f;嗯&#xff0c;对的你没有听错&#xff0c;是 gin 框架。你可能会…

【Java 进阶篇】Java与JQuery选择器:解锁前端开发的魔法大门

在前端开发的世界中&#xff0c;选择器是我们与HTML文档进行互动的钥匙&#xff0c;而Java和JQuery则为我们提供了强大的工具&#xff0c;使得前端开发不再是一个艰深的谜题。本篇博客将围绕Java与JQuery选择器展开&#xff0c;深入解析选择器的奥秘&#xff0c;为你打开前端开…

体验版CorelDRAW2023矢量图话题工具

在当今数字化时代&#xff0c;图形设计已经成为了各行各业不可或缺的一部分。无论是企业的品牌标识、广告宣传&#xff0c;还是个人的插画作品、名片设计&#xff0c;都需要一个强大而多功能的设计软件来实现。而CorelDRAW正是这样一款令人惊叹的工具&#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…

Python开发者的利器:掌握多种执行JS的方法

更多资料获取 &#x1f4da; 个人网站&#xff1a;ipengtao.com JavaScript&#xff08;JS&#xff09;是一种常用的脚本语言&#xff0c;通常用于网页开发&#xff0c;但有时也需要在Python中执行或调用JavaScript代码。这种需求可能是因为希望与网页进行交互&#xff0c;或者…

机器学习数据预处理——Word2Vec的使用

引言&#xff1a; Word2Vec 是一种强大的词向量表示方法&#xff0c;通常通过训练神经网络来学习词汇中的词语嵌入。它可以捕捉词语之间的语义关系&#xff0c;对于许多自然语言处理任务&#xff0c;包括情感分析&#xff0c;都表现出色。 代码&#xff1a; 重点代码&#…

【数据结构】树与二叉树(十二):二叉树的递归创建(算法CBT)

文章目录 5.2.1 二叉树二叉树性质引理5.1&#xff1a;二叉树中层数为i的结点至多有 2 i 2^i 2i个&#xff0c;其中 i ≥ 0 i \geq 0 i≥0。引理5.2&#xff1a;高度为k的二叉树中至多有 2 k 1 − 1 2^{k1}-1 2k1−1个结点&#xff0c;其中 k ≥ 0 k \geq 0 k≥0。引理5.3&…

【Redis系列】Redis的核心命令(上)

哈喽&#xff0c;大家好&#xff0c;我是小浪。那么上篇博客教会了大家如何在Linux上安装Redis&#xff0c;那么本篇博客就要正式开始学习Redis啦&#xff0c;跟着俺的随笔往下看~ 1、启动Redis 那么如何启动Redis呢&#xff1f;最常用的是以下这个命令&#xff1a; redis-cl…

“艾迪-东软杯”第六届武汉理工大学新生程序设计竞赛

A.Capoos Acronym Zero 题目描述 yz 和他的朋友 ea 和 zech 一起养了一群 Capoo。 这些 Capoo 非常聪明&#xff0c;但不知道为什么&#xff0c;它们并没有从三人那里学到怎么写算法题&#xff0c;而是出于某种原因开始研究语言学&#xff0c;并发明了一套自己的暗语。这门暗语…

设计模式之十一:代理模式

代理可以控制和管理访问。 RMI提供了客户辅助对象和服务辅助对象&#xff0c;为客户辅助对象创建和服务对象相同的方法。RMI的好处在于你不必亲自写任何网络或I/O代码。客户程序调用远程方法就和运行在客户自己本地JVM对对象进行正常方法调用一样。 步骤一&#xff1a;制作远程…

【C++初阶】类与对象(三)

目录 一、再谈构造函数1.1 初始化列表1.1.1 初始化列表写法1.1.2 哪些成员要使用初始化列表 1.2 初始化列表的特点1.2.1 队列类问题解决1.2.2 声明顺序是初始化列表的顺序 1.3 explicit关键字1.3.1 explicit关键字的作用 二、static成员2.1 类的静态成员概念2.2 类里创建了多少…

C++ 模板保姆级详解——template<class T>(什么是模板?模板分哪几类?模板如何应用?)

目录 一、前言 二、 什么是C模板 &#x1f4a6;泛型编程的思想 &#x1f4a6;C模板的分类 三、函数模板 &#x1f4a6;函数模板概念 &#x1f4a6;函数模板格式 &#x1f4a6;函数模板的原理 &#x1f4a6;函数模板的实例化 &#x1f34e;隐式实例化 &#x1f349;显式实…

Halcon WPF 开发学习笔记(4):Halcon 锚点坐标打印

文章目录 专栏前言锚点二次开发添加回调函数辅助Model类 下集预告 专栏 Halcon开发 博客专栏 WPF/HALCON机器视觉合集 前言 Halcon控件C#开发是我们必须掌握的&#xff0c;因为只是单纯的引用脚本灵活性过低&#xff0c;我们要拥有Halcon辅助开发的能力 锚点开发是我们常用的…

记录一次某某虚拟机的逆向

导语 学了一段时间的XPosed&#xff0c;发现XPosed真的好强&#xff0c;只要技术强&#xff0c;什么操作都能实现... 这次主要记录一下我对这款应用的逆向思路 apk检查 使用MT管理器检查apk的加壳情况 发现是某数字的免费版本 直接使用frida-dexdump 脱下来后备用 应用分…

【ATTCK】MITRE Caldera - 测试数据泄露技巧

CALDERA是一个由python语言编写的红蓝对抗工具&#xff08;攻击模拟工具&#xff09;。它是MITRE公司发起的一个研究项目&#xff0c;该工具的攻击流程是建立在ATT&CK攻击行为模型和知识库之上的&#xff0c;能够较真实地APT攻击行为模式。 通过CALDERA工具&#xff0c;安全…

Git版本控制系统之分支与标签(版本)

目录 一、Git分支&#xff08;Branch&#xff09; 1.1 分支作用 1.2 四种分支管理策略 1.3 使用案例 1.3.1 指令 1.3.2 结合应用场景使用 二、Git标签&#xff08;Tag&#xff09; 2.1 标签作用 2.2 标签规范 2.3 使用案例 2.3.1 指令 2.3.2 使用示例 一、Git分支&…

55基于matlab的1.高斯噪声2.瑞利噪声3.伽马噪声4.均匀分布噪声5.脉冲(椒盐)噪声

基于matlab的1.高斯噪声2.瑞利噪声3.伽马噪声4.均匀分布噪声5.脉冲&#xff08;椒盐&#xff09;噪声五组噪声模型&#xff0c;程序已调通&#xff0c;可直接运行。 55高斯噪声、瑞利噪声 (xiaohongshu.com)

“第六十六天”

这个我记得是有更优解的&#xff0c;不过还是明天发吧&#xff0c;明天想一想&#xff0c;看看能不能想起来 #include<string.h> int main() {char a[201] { 0 };char b[201] { 0 };scanf("%s %s", a, b);int na strlen(a);int nb strlen(b);int i 0, j …