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

news/2024/5/20 10:05:24 标签: java, javase, jvm, 面试, JUC

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

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

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

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

实现多线程

继承Thread类

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

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

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

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

java">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方法:

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

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

java">public class ThreadDemo {
    public static void main(String[] args) {
        //创建MyRun的对象,表示多线程要执行的任务
        MyRun mr = new MyRun();
        //创建线程对象,并setName
        Thread 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类:

java">public class MyCallable implements Callable<Integer> {

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

2、测试类:

java">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的执行权出让:

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

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

插入线程

观察下面的代码:

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

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

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

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


http://www.niftyadmin.cn/n/5368787.html

相关文章

最新话费充值系统源码,附带系统安装教程

搭建教程 亲测环境&#xff1a;PHP7.0MySQL5.6 PHP扩展安装&#xff1a;sg11 数据库配置文件路径&#xff1a;/config/database.php 伪静态设置为thinkphp 后台地址&#xff1a;/admin 账号密码&#xff1a;admin/123456

JS第一天、数据类型检测、内存释放

复习&#xff1a; 以下类型都是 object console.log(typeof new Object); console.log(typeof new Array()); console.log(typeof new Date()); console.log(typeof new RegExp()); console.log(typeof new String()); console.log(typeof new Number()); console.log(typeof…

在 Docker 中启动 ROS2 里的 rivz2 和 rqt 出现错误的解决方法

1. 出现错误&#xff1a; 运行 ros2 run rivz2 rivz2 &#xff0c;报错如下 &#xff1a; No protocol specified qt.qpa.xcb: could not connect to display :1 qt.qpa.plugin: Could not load the Qt platform plugin "xcb" in "" even though it was f…

C语言中的条件编译:探索编译时的决策

在C语言中&#xff0c;条件编译是一种特殊的编译技术&#xff0c;它允许开发者在编译时根据特定的条件选择性地包含或排除代码片段。这种技术非常有用&#xff0c;特别是当你想针对不同的平台、操作系统或配置编译不同的代码时。 在C语言中&#xff0c;条件编译主要通过预处理…

【Unity】Assets/Plugins/Android(/res、/assets等)文件夹作用

Assets/Plugins/Android&#xff1a;包含 Android 平台的插件文件&#xff0c;如 jar、aar、so 等。 Asets/Plugins/Android/assets&#xff1a;包含 Android 平台的资源文件&#xff0c;如图片、音频等。 相当于src/main/assets文件夹 Assets/Plugins/Android/res&#xff1a;…

面试经典150题 -- 区间(总结)

总的链接 : 面试经典 150 题 - 学习计划 - 力扣&#xff08;LeetCode&#xff09;全球极客挚爱的技术成长平台最经典 150 题&#xff0c;掌握面试所有知识点https://leetcode.cn/studyplan/top-interview-150/ 228 汇总区间 直接用双指针模拟即可 ; class Solution { public…

ubuntu22.04 VMware17.5

一、问题&#xff1a; Before you can run VMware, several modules must be compiled and loaded into the running kernel. GCC GNU C Compiler (gcc) version 12.3.0 was not found. If you installed it in a non-default path you can specify the path below. Otherwi…

qt在pro文件中设置utf-8编码

在 Qt 的 .pro 文件中设置使用 UTF-8 编码&#xff0c;可以通过在 .pro 文件中添加以下内容来实现&#xff1a; QMAKE_CXXFLAGS -source-charset UTF-8 QMAKE_CXXFLAGS -execution-charset UTF-8这样设置后&#xff0c;Qt 会将源代码和执行时的字符集都设置为 UTF-8 编码。这…