栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

线程学习(2)-Java线程创建

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

线程学习(2)-Java线程创建

线程创建方式

线程创建方式,划分为三种,Thread创建,Runnable创建,以及futureTask整合Callable来实现访问,代码如下:

package com.bo.threadstudy.two;


import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.Test;
import org.openjdk.jmh.annotations.*;

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;


@Slf4j
//查看平均耗时
@BenchmarkMode(Mode.AverageTime)
//执行次数,设置为5
@Measurement(iterations = 5)
//预热次数,设置为3
@Warmup(iterations = 3)
public class CreateThreadTest {

    
    @Test
    @Benchmark
    public void createThreadTest(){
        Thread thread = new Thread("线程创建"){
            @Override
            public void run() {
                log.debug("执行线程");
            }
        };
        thread.start();
        log.debug("执行主程");
    }

    
    @Test
    @Benchmark
    public void createRunnableTest(){
        new Thread(() -> log.debug("使用runnable来创建线程"), "runnable创建").start();
        log.debug("主程游走");
    }

    
    @Test
    public void createFutureTaskTest(){
        FutureTask futureTask = new FutureTask<>(() -> {
            log.debug("测试call方法正在走");
            return "call方法返回结果";
        });
        Thread thread = new Thread(futureTask, "futureTask创建线程");
        thread.start();

        log.debug("主程正在执行");
        String s = null;
        try {
            s = futureTask.get();
            log.debug("主程获取到结果");
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
        log.debug("futureTask获取到的结果为"+s);
    }

}

简要对比下三种创建的方式。

以Thread创建线程,采用了继承的方式来重写了Thread类之种的run方法,相当于将线程与任务写到了一块。因为java底层类继承仅支持单继承,所以这种方案会导致耦合性较高,对于业务后续扩展并不友好。

以Runnable整合Thread来创建线程,Runnable相当于任务对象,Thread相当于线程对象,这种方式将线程对象与任务这种行为给划分开来,实现了解耦,保证了系统的后期扩展。

以FutureTask结合Callable来整合到Thread中来创建线程,这种方案与Runnable方式类似,只不过我们可以使用FutureTask的get()方法来得到Callable之中的返回结果,从一定程度上保证了线程通信。(类似于线程内部的join方法,等线程执行完成后再执行当前线程,不过这个没有返回结果)。

源码 run方法解析

简单看一下源码,难的我也捋不明白。Thread类下的run方法。

Run方法中依赖于target中的run方法,那么这个target对象是什么,

 

 这个target对象其实就是一个Runable对象,也就是我们第二种创建线程的方式。试想一下,重写

相当于在原有代码上进行改动,而使用Runable则相当于另外定义了接口,在接口中实现该功能。这种方式的实现模式是策略模式。

Runable方法就不介绍了,一个接口,就是为了实现这个run方法来创建的接口。

那么FutureTask结合Callable的实现,底层调用的也是这个run方法,那么FutureTask类与Runnable类是否有一定的联系。

 

 源码种,FutureTask类是实现了Runable类,并整合了泛型来进行操作。而FutureTask中的run方法代码如下:

 底层调用的其实是callable中传入的call方法,根据内部实际实现的call方法来执行操作。

所以Thread类中的run方法根据不同入参传入时执行run方法的操作如上述流程。

FutureTask的get方法

我刚才说了,get方法拥有阻塞当前线程的作用,那么是怎么阻塞的。

 在这里写了个死循环(自旋方式,也就是CAS),只有state这个状态值满足大于COMPLETING时,才可以跳出这个循环。

那么这个状态在哪里改动呢,既然要当前线程执行完后,get方法才能得到返回结果,那么去run方法接着看看。

 刚才在run方法中,我们看到内部有一个set方法。具体源码如下:

 这个方法底层是一个native方法,是在这一步,对state状态进行偏移,然后转化为NORMAL状态。从而让get方法执行结束。

中间涉及的state,aqs这个后续随着深入慢点了解,先不着急。

发现一个问题,在测试类中使用Thread.sleep()方法时。设置睡眠时间一长,会导致sleep后续的程序全部不执行,这个得看看。

 

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/915316.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号