创建和运行线程


+比如有三个计算,耗时如下
image.png
单核CPU,要顺序执行,就是10+11+9+1=31ms
而加入有个四核CPU,执行用时就是11+1=12ms

案例验证

有一个数组,容量一亿,都是1,分别用单线程和多线程求和一亿次。
image.png原数组
C方法:
image.png
创建了四个线程,每个线程计算2500万个数,最终把结果汇总起来
D方法:
image.png一个线程计算一亿个数

结果:在本机上(本机为多核CPU,可以提现多线程比单线程好)
image.png
C方法速度快
在Linux上(Linux是单线程的,多线程单线程都一样)
image.png

结论

单核CPU中多线程不能实际提高程序运行效率,但是可以让CPU在不同线程切换,不至于一个线程总占用CPU。

以上是线程的应用,接下来是线程的基础知识

线程基础知识

创建和运行线程

每个Java程序一启动的时候就创建了一个线程,这个线程就是主线程main
如果在主线程之外还想再创建线程,可以用如下方法

方法一:用Thread对象创建和启动线程(继承Thread)

创建一个Thread的子类,重写run方法,run方法中是线程要执行的任务代码,
这样只是创建了Java中的线程对象,还没有和操作系统的线程相关联
还需要启动线程,调用Thread的start方法。

package com.zqh.day1;

import lombok.extern.slf4j.Slf4j;

/**
 * 使用Thread类创建线程
 */
@Slf4j(topic = "c.CreateThread1")
public class CreateThread1 {
    // Java 启动时默认使用的主线程
    public static void main(String[] args) {
        // 除了主线程,还要创建线程时,用Thread对象
        Thread t = new Thread(){
            @Override
            public void run() {
                // t线程执行的代码
                log.debug("t running");
            }
        };
        // 为线程设置线程名
        t.setName("t1");
        // 启动线程
        t.start();
        // 主线程的输出
        log.debug("main running");
    }
}

方法二:使用Runnable配合Thread

这种方法就是把创建线程和线程执行的任务这两步给分开了
【Thread:线程】
【Runnable:任务】
Runnable源码:image.png

package com.zqh.day1;

import lombok.extern.slf4j.Slf4j;

/**
 * 通过Runnable+Thread创建线程,把任务和线程分开
 * 更为灵活
 */
@Slf4j(topic = "c.CreateThread2")
public class CreateThread2 {
    // 主线程
    public static void main(String[] args) {
        // 创建Runnable对象(任务对象)
        Runnable r = new Runnable() {
            // 任务
            @Override
            public void run() {
                log.debug("task running");
            }
        };
        // 线程对象,把任务对象作为参数传进构造方法,第二个参数为线程名
        Thread t = new Thread(r,"t2");
        // 启动线程
        t.start();
    }

}

执行结果
image.png
只有一个抽象方法的接口可以用lambda简化
简化流程:

// 创建Runnable对象(任务对象)
        Runnable r = new Runnable() {
            // 任务
            @Override
            public void run() {
                log.debug("task running");
            }
        };

变成

// 创建Runnable对象(任务对象)
Runnable r = ()->{
    log.debug("task running");
};
方法一和方法二的区别

Runnable会给Thread传一个target,如果target不为空,就target.run();
走的都是run方法
用Runnable可以与高级API配合,更灵活

方法三:FutureTask配合Thread

FutureTask可以获取任务的执行结果

public interface RunnableFuture<V> extends Runnable, Future<V>

可以看到,FutureTask继承了Runnable,也可以当做一个任务对象,那么与Runnable不同的是,还多继承了一个Future接口
Future接口是用来返回执行结果的。可以在两个线程之间,把一个结果传给另外一个线程

FutureTask对象的创建还需要配合一个Callable类型的参数来创建。
Callable源码

@FunctionalInterface
public interface Callable<V> {
    /**
     * Computes a result, or throws an exception if unable to do so.
     *
     * @return computed result
     * @throws Exception if unable to compute a result
     */
    V call() throws Exception;
}

可以看到,和Runnable很像,但是他可以返回结果抛出异常
这是Runnable,不能返回结果和抛异常

@FunctionalInterface
public interface Callable<V> {
    /**
     * Computes a result, or throws an exception if unable to do so.
     *
     * @return computed result
     * @throws Exception if unable to compute a result
     */
    V call() throws Exception;
}

FutureTask+Thread创建线程代码:

package com.zqh.day1;

import lombok.extern.slf4j.Slf4j;

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

@Slf4j
public class CreateThread3 {
    // 主线程
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        // 创建可返回结果+抛异常的任务对象FutureTask
        // 可以有泛型,泛型就是返回结果的类型
        FutureTask<Integer> task = new FutureTask<>(new Callable<Integer>() {
            // Callable 的call方法,里面是线程具体执行的任务
            @Override
            public Integer call() throws Exception {
                log.debug("callable is running");
                // 休眠一秒钟返回结果100
                Thread.sleep(1000);
                return 100;
            }
        });
        /*
           还是得借助Thread执行这个线程,把task传给Thread的构造方法
           因为FutureTask也实现了Runnable,所以可以作为参数传给Thread
         */
        Thread t = new Thread(task,"t3");
        t.start();
        // 线程返回了100,但是主线程还没用到,现在用主线程打印t线程的返回值
        // 用task任务对象的get方法,主线程走到这里时会等待t线程的结果返回
        // 所以走到这里会等待1秒
        log.debug("{}",task.get());
    }
}

观察多个线程同时执行

image.png
t1和t2都是死循环执行输出running这句话
执行后:
交替执行。
image.png

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值