Three ways to achieve java thread (the most easy-to-understand in history)

Three ways of threading

  • Implement Runnable interface (underlying implementation)

  • Inherit the Thread class

  • Implement Callable interface (advanced)


Implement Runnable interface

​ First look at the Runnable source code, which is available in our 1.0 version and under the java.lang package

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();
}

As can be seen from the figure above, the Runnable interface is only a non-returning run method, and it is a functional interface modification class, so we can use lambda expressions to abbreviate the code.

Implement the Runnable interface to create a thread.

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();
    }
}

Executing the main method can tell that we find that the thread is not safe (the number of printed tickets is greater than 10). Due to the copy operation between the working memory and the main memory, the data in the working memory will be incorrect.

Inherit the Thread class

We found that the Thread class implements the Runnable interface. Why do we need to write a Thread if we have Runnable?

Insert picture description here

Because we defined a Runnable interface and didn't really create a thread, it was just an ordinary Java interface, and the real function of creating a thread is in the Thread class.

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();
    }
}

Simple implementation of the Callable interface

First, take out the callable source code file, which is only available after version 5 and is in the JUC (java.util.concurrent) package

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;
}

We can see that Callbale and Runnable are very similar, both are an interface and a functional interface (only one method). It is worth noting that the call method of Callable has a return value, a method that can throw an exception; Future will talk about it in another article.

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();
    }
}

Experience the benefits of asynchronous processing brought by Callable

Requirements: I log in to Taobao, get points information, get shipping address information, and get order information.

Analyze the requirements: The above three requirements have no dependencies and no order requirements.

Original implementation:

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<>();
    }
}

Console printing: 10007ms

We can know that the program is serial, so it should be about 3+2+5=10 seconds! The more business volume, the longer it takes!

We use multi-threaded parallel processing

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();
    }
}

The result of the execution is: 5009ms, worthy of me ! !

After the program is parallel, if this method needs to call three services, the longest service time will be the bottleneck of the system, not the amount of business!

Guess you like

Origin blog.csdn.net/qq_44112474/article/details/108542012