多线程JUC:多线程的实现和常用成员方法(守护、礼让、插入线程)

👨‍🎓作者简介:一位大四、研0学生,正在努力准备大四暑假的实习
🌌上期文章:首期文章
📚订阅专栏:多线程&JUC
希望文章对你们有所帮助

JUC的学习也是需要一些计算机、操作系统的知识的,也算是比较重要的吧,其实自己也是接触了不少的,包括之前做Redis的项目的时候也是老摸这些玩意。但是还没有非常系统的学过,为了即将到来的面试,JUC抓紧速成一波。

多线程的实现和常用成员方法

  • 实现多线程
    • 继承Thread类
    • 实现Runnable接口
    • 利用Callable和Future接口
    • 总结
  • 多线程的常用成员方法
    • 线程优先级
    • 守护线程
    • 礼让线程
    • 插入线程

实现多线程

继承Thread类

将类声明为Thread类的子类,该子类应重写Thread类的new方法,接下来就可以分配并启动该子类的实例,开启一个线程。

这里实现一个简单的Demo:
1、创建Thread类的子类:

public class MyThread extends Thread{@Overridepublic void run() {//线程要执行的代码for (int i = 0; i < 100; i++) {System.out.println(this.getName() + "Hello World!");}}
}

2、创建对象开启实例,观察结果:

public class ThreadDemo {public static void main(String[] args) {MyThread t1 = new MyThread();MyThread t2 = new MyThread();//给线程起名字,方便观察运行结果t1.setName("线程1");t2.setName("线程2");//开启线程t1.start();t2.start();}
}

最终运行结果显示线程1和线程2是并行执行,而不是串行的了。

实现Runnable接口

先自己定义一个类,这个类实现了Runnable接口,并重写run方法。接着在测试类中创建这个类的对象,再创建一个Thread类的对象,并开启线程。

1、定义MyRun类并实现Runnable接口,由于这个类没有继承Thread类,所以不能使用getName方法,但是Thread类本身具有静态方法currentThread可以获取当前在执行任务的线程的对象,因此可以通过这个对象来执行getName方法:

public class MyRun implements Runnable{@Overridepublic void run() {for (int i = 0; i < 100; i++) {//获取当前线程的对象Thread thread = Thread.currentThread();System.out.println(thread.getName() + "Hello World");}}
}

2、测试类中创建该类的对象、Thread类的对象,并开启线程:

public class ThreadDemo {public static void main(String[] args) {//创建MyRun的对象,表示多线程要执行的任务MyRun mr = new MyRun();//创建线程对象,并setNameThread t1 = new Thread(mr);t1.setName("线程1");Thread t2 = new Thread(mr);t2.setName("线程2");//开启线程t1.start();t2.start();}
}

利用Callable和Future接口

第三种实现方式实际上是对前面两种方法的补充。
前面两种实现方式都重写了run,但是其没有返回值,我们就无法获取多线程运行的结果。
第三种实现方式的特点就是可以获取多线程运行结果

其实现的流程:

1、创建一个MyCallable类实现Callable接口
2、重写call方法,方法是有返回值的,表示多线程运行的结果
3、编写测试类:
(1)创建MyCallable对象表示多线程要执行的任务
(2)创建Future对象,该对象可以管理多线程运行的结果,由于Future是一个接口无法创建对象,所以要创建其实现类FutureTask的对象
(3)创建Thread对象,再开启线程

1、MyCallable类:

public class MyCallable implements Callable<Integer> {@Overridepublic Integer call() throws Exception {//求1-100之间的和int sum = 0;for (int i = 0; i <= 100; i++) {sum += i;}return sum;}
}

2、测试类:

public class ThreadDemo {public static void main(String[] args) throws ExecutionException, InterruptedException {//创建MyRun的对象,表示多线程要执行的任务MyCallable mc = new MyCallable();//创建FutureTask对象FutureTask<Integer> ft = new FutureTask<>(mc);//创建线程对象Thread t = new Thread(ft);//开启线程t.start();Integer result = ft.get();System.out.println(result);}
}

总结

1、继承Thread:
(1)优点:变成简单,可以直接使用Thread类中的方法
(2)缺点:可拓展性差,不能再继承其他的类
2、实现Runnable接口:
(1)优点:拓展性强,实现该接口的同时还能继承其他的类
(2)编程相对复杂,不能直接使用Thread类中的方法
3、实现Callable接口:
(1)除了Runnable接口的优点,还可以观察线程运行的结果
(2)编程相对复杂,不能直接使用Thread类中的方法

多线程的常用成员方法

下面是多线程常用的一些成员方法,其实很多也都是见过的:

方法名称说明
String getName()返回线程名称
void setName(String name)设置线程名,也可以使用构造方法
static Thread currentThread()获取当前线程对象
static void sleep(long time)让线程休眠指定的时间,单位为毫秒
setPriority(int newPriority)设置线程优先级
final int getDaemon(boolean on)设置为守护线程
public static void yield()礼让线程
public static void join()插入进程

对于前4种方法,有一些注意点和细节:

1、若没有给线程设置名字,使用getName也是会有默认的名字的,格式为Thread-X(X为序号,从0开始),可以自行去底层看一下
2、除了可以用setName可以给线程设置名字,也可以使用构造函数MyThread t = new MyThread("线程");,但是需要保证MyThread里面的构造函数内部使用super关键字,从而重写父类的构造方法。
3、如果我们没有启动线程,执行Thread.currntThread(),也可以获取到线程,这个线程的名称即为main,即JVM虚拟机启动以后,会自动启动多条线程,其中有一条就是main线程,作用是调用main方法,并执行里面的代码
4、sleep():
(1)哪条线程执行到这个方法,哪条线程就睡眠
(2)方法的参数就表示睡眠的时间,单位为毫秒
(3)当时间到了以后,线程会自动被唤醒,继续执行下面的代码

线程优先级

首先需要了解一下操作系统中的系统调度,调度分为抢占式调度和非抢占式调度,在java虚拟机中采用的是抢占式调度,因此可以给线程设置优先级,优先级越大,抢占到CPU、被执行的概率就越大。
线程的优先级可以设置为1-10,默认为5。
在这里插入图片描述
注意:

1、可以使用getPriority来获取线程的优先级
2、优先级越高只能代表该线程被执行的概率越大,而不代表一定会被执行

守护线程

守护线程这个名称听起来不是很好理解,可以想象成是一个备胎线程,当其他的非守护线程执行完毕后,守护线程其实根本没有执行的必要了,会陆续的结束。将线程设置为守护线程调用t.setDaemon(true)即可。
这其实还是有应用场景的,比如QQ中的聊天和发送文件可以视为2个线程,当我们把聊天的线程关闭了以后,线程2就没有执行的必要了,就会自动终止。

礼让线程

我们之前设置了两个线程来执行相同的任务,可以发现两个线程确实是交替执行的,但是经常会出现线程1连续输出了好多内容这种情况,出现的原因就是因为线程的执行是有概率性的,而礼让线程表示将当前CPU的执行权出让:

public class MyThread extends Thread{@Overridepublic void run() {for (int i = 0; i < 100; i++) {System.out.println(this.getName());//出让当前CPU的执行权Thread.yield();}}
}

但这里也只能尽量让结果均衡,但是还是可能会出现那种情况,所以这种方法用的少。

插入线程

观察下面的代码:

MyThread t = new MyThread();
t.setName("土豆");
t.start();
for(int i = 0; i < 10 ; ++i) {System.out.println("main线程" + i);
}

最后执行会先执行main线程,毕竟执行的就是main函数。
而使用join方法就可以将其插入到main线程之前,使得其先被执行:

t.join();//把t线程插入到当前线程之前,在这里当前线程就是main线程

这个方法用的也不多,只需要了解一下就好了。

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Redis发布订阅及事务管理

目录 1.1 发布订阅 1.1.1 什么是发布订阅 1.1.2 常用命令 1.1.3 示例演示 1.2 事务管理 1.2.1 事务定义 1.2.2 Multi、Exec、discard 1.2.3 示例 1.2.4 事务的错误处理 1.2.5 事务的冲突问题 1.2.5.1 事务场景 1.2.5.2 悲观锁 1.2.5.3 乐观锁 1.2.5.4 事务解决冲…

详解C++类和对象(下)完结篇

文章目录 写在前面1. 进一步认识构造函数1.1 初始化列表1.2 初始化列表的特性1.3 explicit关键字 2. static成员变量和static成员函数2.1 static成员的概念2.2 static成员的特性 3. 友元3.1 友元函数3.1 友元类 4. 内部类5.匿名对象 写在前面 本篇文章详细介绍了C类和对象中几…

vector类的模拟实现

实现基本的vector框架 参考的是STL的一些源码&#xff0c;实现的vector也是看起来像是一个简略版的&#xff0c;但是看完能对vector这个类一些接口函数更好的认识。 我们写写成员变量&#xff0c;先来看看STL的成元变量是那些 namespace tjl {template<class T>class …

作业2.7

C基础补习测试题 一、选择题&#xff08;每题2分&#xff0c;共 50 分&#xff09; C语言中&#xff0c;运算对象必须是整型数的运算符是 A 。 A、% B、/ C、%和/ D、* 若有关系x≥y≥z,应使用 A C语言表达式。 A、(x>y)&&(y>z)…

python环境配置(操作简单)

文章目录 下载文件链接过时请私信我 安装python3.9.0下载安装步骤安装python3.9.0步骤 安装pyCharm2023.3.3下载链接安装方法 激活激活链接激活方法 创建项目出现问题 下载文件 链接过时请私信我 链接&#xff1a;https://pan.baidu.com/s/1ORuhvSbBoTxeQfnYhOuiFA?pwd3oan …

Java应用中各类环境变量的优先级及最佳实践

1.引言 Java应用程序的开发和部署过程中&#xff0c;合理利用各类环境变量是关键之一。不同类型的环境变量&#xff0c;如系统环境变量、进程级环境变量、Java启动参数设置的系统属性以及Spring Boot配置文件中的环境变量&#xff0c;它们之间存在优先级差异。 深入理解这些环…

第1章 认识Flask

学习目标 了解Flask框架&#xff0c;能够说出Flask框架的发展史以及特点 熟悉隔离Python环境的创建方式&#xff0c;能够独立在计算机上创建隔离的Python环境 掌握Flask的安装方式&#xff0c;能够独立在计算机上安装Flask框架 掌握PyCharm配置隔离环境的方式&#xff0c;能…

27/100两数相除(位移todo)

题目 27/100两数相除 给你两个整数&#xff0c;被除数 dividend 和除数 divisor。将两数相除&#xff0c;要求 不使用 乘法、除法和取余运算。 整数除法应该向零截断&#xff0c;也就是截去&#xff08;truncate&#xff09;其小数部分。例如&#xff0c;8.345 将被截断为 8 …

【新书推荐】6.4节 标志寄存器

本节内容&#xff1a;8086 CPU内的标志寄存器FLAG为16位寄存器。本节我们将讲述FLAG寄存器各个标志位的使用方法。 ■标志寄存器的标志位&#xff1a;标志位可以分为6个状态标志位和3个控制标志位&#xff0c;此外还有7个保留的标志位。状态标志位用来记录算术逻辑运算的结果。…

C# CAD交互界面-自定义面板集(四)

运行环境 vs2022 c# cad2016 调试成功 一、引用 using Autodesk.AutoCAD.Runtime; using Autodesk.AutoCAD.Windows; using System.Windows.Forms; 二、程序说明 创建自定义面板集&#xff08;PaletteSet&#xff09;的C#命令方法实现。该方法名为CreatePalette&#xff…

Nacos(2)

Nacos部署 服务器端docker部署&#xff08;需要服务器安装好docker&#xff09; 导入sql文件到服务器编写nacos配置文件custom.env&#xff08;示例如下&#xff0c;改为自己服务器nacos相关信息&#xff09; PREFER_HOST_MODEhostname MODEstandalone SPRING_DATASOURCE_PL…

Git介绍和常用命令说明

目录 一、Git概述 1.1 Git是什么 1.2 Git有什么用 1.3 Git仓库介绍 二、Git下载与安装 三、Git代码托管服务&#xff08;远程仓库&#xff09; 四、Git常用命令 4.1 设置用户信息 4.2 获取Git仓库 4.2.1 本地初始化Git仓库 4.2.2 从远程仓库克隆 4.3 本地仓库操作 …