java线程实现的三种方式(史上最通俗易懂)

线程实现的三种方式

  • 实现Runnable接口(底层 实现)

  • 继承Thread类

  • 实现Callable接口(进阶)


实现Runnable接口

​ 先看Runnable源码,是我们1.0版本就有的且在java.lang包下的

package java.lang;
/*
 * @author  Arthur van Hoff
 * @see     java.lang.Thread 线程类继承了Runnable
 * @see     java.util.concurrent.Callable 被包装成Thread才能运行
 * @since   JDK1.0
 */
@FunctionalInterface // 这是函数式接口
public interface Runnable {
    
    
    /**
     * When an object implementing interface <code>Runnable</code> is used
     * to create a thread, starting the thread causes the object's
     * <code>run</code> method to be called in that separately executing
     * thread.
     * <p>
     * The general contract of the method <code>run</code> is that it may
     * take any action whatsoever.
     *
     * @see     java.lang.Thread#run()
     */
    public abstract void run();
}

由上图可知,Runnable接口仅此一个无返回值的run方法,并且是函数式接口修饰类,那么我们可以用lambda表达式来简写代码。

实现Runnable接口创建一个线程。

public class ThreadDemo2 implements Runnable {
    
    
    private static int ticket = 10;

    @Override
    public void run() {
    
    
        while (ticket > 0) {
    
    
            // Thread.currentThread().getName()获取当前操作该类的线程名
            System.out.println(Thread.currentThread().getName() + "拿走第" + ticket--);
        }
    }

    public static void main(String[] args) {
    
    

        ThreadDemo2 threadDemo1 = new ThreadDemo2();
        // 如果直接run就是调用一个普通类的一个方法,就体会不到多线程抢占时间片
//          threadDemo1.run();
        // 把实现Runnable接口的类当作Thread类的构造方法入参,此时,多个Thread传如同一个入参
        new Thread(threadDemo1, "线程1").start();
        new Thread(threadDemo1, "线程2").start();
    }
}

执行main方法可以 得知我们发现线程不安全(打印票数大于10),由于工作内存和主内存之间进行copy操作,会导致工作内存的数据不正确。

继承Thread类

我们发现Thread类实现了Runnable接口,为什么有了Runnable还要写个Thread尼?

在这里插入图片描述

因为我们定义个Runnable接口并没有真正创建线程,只是一个普通的Java接口而已,而真正实现我们创建线程的功能都在Thread类中。

public class ThreadDelete extends Thread {
    
    
    
    @Override
    public void run() {
    
    
        for (int i = 0; i < 20; i++) {
    
    
            System.out.println(Thread.currentThread().getName()+"执行了"+i);
        }
    }

    public static void main(String[] args) {
    
    
        ThreadDelete threadDelete1 = new ThreadDelete();
        threadDelete1.setName("线程A");
        threadDelete1.start();
        ThreadDelete threadDelete2 = new ThreadDelete();
        threadDelete2.setName("线程B");
        threadDelete2.start();
    }
}

简单实现Callable接口

首先拿出 callable 源码文件,是5版本后才有的并在在JUC(java.util.concurrent)包下

package java.util.concurrent;

/**
 * A task that returns a result and may throw an exception.
 * Implementors define a single method with no arguments called
 * {@code call}.
 *
 * <p>The {@code Callable} interface is similar to {@link
 * java.lang.Runnable}, in that both are designed for classes whose
 * instances are potentially executed by another thread.  A
 * {@code Runnable}, however, does not return a result and cannot
 * throw a checked exception.
 *
 * <p>The {@link Executors} class contains utility methods to
 * convert from other common forms to {@code Callable} classes.
 *
 * @see Executor
 * @since 1.5
 * @author Doug Lea
 * @param <V> the result type of method {@code call}
 */
@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;
}

我们可以看到,Callbale和Runnable非常相似,都是一个接口且是函数式接口(只有一个方法)。值得注意的是Callable的call方法是有返回值的,可以抛出异常的一个方法;Future会在另一篇文章上讲。

public class ThreadDemo4 implements Callable<Boolean> {
    
    

    @Override
    public Boolean call() throws Exception {
    
    
        System.out.println("执行的线程是:" + Thread.currentThread().getName());
        return true;
    }

    public static void main(String[] args) throws ExecutionException, InterruptedException {
    
    
        ThreadDemo4 t1 = new ThreadDemo4();
        ThreadDemo4 t2 = new ThreadDemo4();
        ThreadDemo4 t3 = new ThreadDemo4();
        // 创建一个线程池
        ExecutorService service = Executors.newFixedThreadPool(5);
        // 提交一个任务给操作系统,返回一个异步结果
        Future<Boolean> r1 = service.submit(t1);
        // 这里的get是一个阻塞的,如果有值才会返回
        System.out.println("获取线程的返回值为"+r1.get());
        service.submit(t2);
        service.submit(t3);
        // 使用lambda表达式简化书写
        System.out.println(service.submit(() -> {
    
    
            System.out.println("这是lambda表达式实现线程"+Thread.currentThread().getName());
            // 返回值是个泛型
            return 1 > 2 ? "returnSomething" : 3;
        }).get());
        // 关闭线程池
        service.shutdown();
    }
}

体会Callable带来的异步处理好处

需求:我登录淘宝,获得积分信息,获得收货地址信息,获得订单信息。

分析需求:以上三个需求并没有依赖关系,顺序没有要求。

原始实现:

package com.yang;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;

/**
 * @author: fudy
 * @date: 2020/9/11 下午 08:18
 * @Decription: 模拟登录淘宝获取积分信息,收获地址,订单信息
 **/
public class IndexController {
    
    
    public static void main(String[] args) throws InterruptedException {
    
    
        // 获取当前系统毫秒值
        long currentBeginTime = System.currentTimeMillis();
        // 模拟服务执行
        Integral.getIntegral();
        ShippingAddress.getShippingAddress();
        OrderInfo.getOrderInfo();
        long currentEndTime = System.currentTimeMillis();
        System.out.println("程序执行了:"+(currentEndTime-currentBeginTime)+"ms");
    }

}
class Integral{
    
    
    public static Long getIntegral() throws InterruptedException {
    
    
        // 模拟查询数据库以及逻辑处理操作
        TimeUnit.SECONDS.sleep(3);
        System.out.println("获取用户积分为10积分");
        return 10L;
    }
}
class ShippingAddress{
    
    
    public static List<String> getShippingAddress() throws InterruptedException {
    
    
        TimeUnit.SECONDS.sleep(2);
        System.out.println("获取用户收获地址信息");
        return new ArrayList<>();
    }
}
class OrderInfo{
    
    
    public static List<String> getOrderInfo() throws InterruptedException {
    
    
        TimeUnit.SECONDS.sleep(5);
        System.out.println("获取用户订单信息");
        return new ArrayList<>();
    }
}

控制台打印:10007ms

我们可以知道,程序是串行的,那么应该是3+2+5=10秒左右!业务量越多,则耗时越长!

我们利用多线程并行处理

package com.yang;

import java.util.List;
import java.util.concurrent.*;

/**
 * @author: fudy
 * @date: 2020/9/11 下午 08:29
 * @Decription:
 **/
public class IndexPlusController{
    
    
    public static void main(String[] args) throws InterruptedException, ExecutionException {
    
    
        long currentBeginTime = System.currentTimeMillis();
        // 利用Callable多线程执行
        // 创建线程池
        ExecutorService service = Executors.newFixedThreadPool(5);
        // 执行任务
        Future<Long> longFuture = service.submit(new IntegralCallable());
        Future<List<String>> listFuture = service.submit(new ShippingAddressCallable());
        Future<List<String>> submit = service.submit(new OrderInfoCallable());
        // 此处get方法是阻塞的,不能 List<String> shippingAddress =  service.submit(new ShippingAddressCallable()).get();
        List<String> shippingAddress = listFuture.get();
        Long integral = longFuture.get();
        List<String> orderInfo = submit.get();
        long currentEndTime = System.currentTimeMillis();
        System.out.println("程序执行了:"+(currentEndTime-currentBeginTime)+"ms");
        service.shutdown();
    }
}
class IntegralCallable implements Callable<Long>{
    
    

    @Override
    public Long call() throws Exception {
    
    
        return Integral.getIntegral();
    }
}
class ShippingAddressCallable implements Callable<List<String>> {
    
    

    @Override
    public List<String> call() throws Exception {
    
    
        return ShippingAddress.getShippingAddress();
    }
}
class OrderInfoCallable implements Callable<List<String>> {
    
    
    @Override
    public List<String> call() throws Exception {
    
    
        return OrderInfo.getOrderInfo();
    }
}

执行结果是:5009ms,不愧是我!!

程序并行后,如果此方法需要调用三个服务,那么服务耗时最长的时间会作为系统的瓶颈,而不是业务量的多少!

猜你喜欢

转载自blog.csdn.net/qq_44112474/article/details/108542012