JAVA多线程4种实现方式

admin
2022-03-10 / 0 评论 / 127 阅读 / 正在检测是否收录...
温馨提示:
本文最后更新于2022年03月26日,已超过733天没有更新,若内容或图片失效,请留言反馈。

异步与线程池

1、初始化线程的4种方式

1)、继承Thread
2)、实现Runnable接口
3)、实现Callable接口+FutureTask(可以拿到返回结果,可以处理异常)
4)、线程池

方式1和方式2:主进程无法获取线程的运算结果。不适合当前场景。

方式3:主进程可以获取线程的运算结果,但是不利于控制服务器中的线程资源。可以导致服务器资源耗尽。
方式4:通过如下两种方式初始化线程池

Executors.newFiexed ThreadPool(3);
//或者
new ThreadPoolExecutor(corePoolSize,maximumPoolSize,keepAliveTime,TimeUnit unit, workQueue,threadFactory,handler);

方式1、继续Thread

/**
 * @description: 继承Thread
 * @author: <a href="mailto:batis@foxmail.com">清风</a>
 * @date: 2022/3/10 21:06
 * @version: 1.0
 */
public class MyThread {
    public static void main(String[] args) {
        System.out.println("启动main方法开始");

        OneMyThread oneMyThread = new OneMyThread();
        oneMyThread.start();

        System.out.println("结束main方法");

    }

    public static class OneMyThread extends Thread{
        @Override
        public void run() {
            System.out.println("当前线程:"+Thread.currentThread().getId());
            int i = 10/2;
            System.out.println("运行结果:"+i);
        }
    }
}

运行结果:

启动main方法开始
结束main方法
当前线程:22
运行结果:5

方式2、实现Runable接口

/**
 * @description: 实现Runnable接口
 * @author: <a href="mailto:batis@foxmail.com">清风</a>
 * @date: 2022/3/10 21:06
 * @version: 1.0
 */
public class MyThread {
    public static void main(String[] args) {
        System.out.println("启动main方法开始");
        
        TwoMyThread twoMyThread = new TwoMyThread();
        new Thread(twoMyThread).start();

        System.out.println("结束main方法");

    }

    /**
     * 实现Runnable接口
     */
    public static class TwoMyThread implements Runnable {

        @Override
        public void run() {
            System.out.println("当前线程:"+Thread.currentThread().getId());
            int i = 10/2;
            System.out.println("运行结果:"+i);
        }
    }
}

运行结果:

启动main方法开始
结束main方法
当前线程:22
运行结果:5

方式3、实现Callable泛型接口

/**
 * @description: 继承Thread
 * @author: <a href="mailto:batis@foxmail.com">清风</a>
 * @date: 2022/3/10 21:06
 * @version: 1.0
 */
public class MyThread {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        System.out.println("启动main方法开始");

        FutureTask<Integer> futureTask = new FutureTask<>(new ThreeMyThread());
        new Thread(futureTask).start();
        //等待线程执行完成,返回结果
        Integer i = futureTask.get();
        System.out.println("线程执行完,返回结果:"+i);

        System.out.println("结束main方法");

    }

    /**
     * 实现Callable<T>泛型接口
     */
    public static class ThreeMyThread implements Callable<Integer> {

        @Override
        public Integer call() throws Exception {
            System.out.println("当前线程:"+Thread.currentThread().getId());
            int i = 10/2;
            System.out.println("运行结果:"+i);
            return i;
        }
    }
}

运行结果:

启动main方法开始
当前线程:22
运行结果:5
线程执行完,返回结果:5
结束main方法

注意:结果的顺序,可以看到是一个阻塞等待

通过FutureTask类源码可以看到,FutureTask不仅可以接受Callable还可以接收Runnable.

FutureTask源码如下:

public class FutureTask<V> implements RunnableFuture<V> {
    /**
     * Creates a {@code FutureTask} that will, upon running, execute the
     * given {@code Runnable}, and arrange that {@code get} will return the
     * given result on successful completion.
     *
     * @param runnable the runnable task
     * @param result the result to return on successful completion. If
     * you don't need a particular result, consider using
     * constructions of the form:
     * {@code Future<?> f = new FutureTask<Void>(runnable, null)}
     * @throws NullPointerException if the runnable is null
     */
    public FutureTask(Runnable runnable, V result) {
        this.callable = Executors.callable(runnable, result);
        this.state = NEW;       // ensure visibility of callable
    }

FutureTask实现RunnableFuture接口:RunnableFuture最终是继承的Runnable。

package java.util.concurrent;

/**
 * A {@link Future} that is {@link Runnable}. Successful execution of
 * the {@code run} method causes completion of the {@code Future}
 * and allows access to its results.
 * @see FutureTask
 * @see Executor
 * @since 1.6
 * @author Doug Lea
 * @param <V> The result type returned by this Future's {@code get} method
 */
public interface RunnableFuture<V> extends Runnable, Future<V> {
    /**
     * Sets this Future to the result of its computation
     * unless it has been cancelled.
     */
    void run();
}

方式4、直接提交线程池,线程池会自动开启任务。

每次通过new Thread()创建线程问题:导致资源耗尽。以上三种启动方式都不用。应该将所有异步多线程任务交给线程池执行。

线程池:

package com.yanxizhu.family.booking;

import java.util.concurrent.*;

/**
 * @description: 继承Thread
 * @author: <a href="mailto:batis@foxmail.com">清风</a>
 * @date: 2022/3/10 21:06
 * @version: 1.0
 */
public class MyThread {

    //应保证,当前系统中线程池只有一两个,每个异步任务,提交给线程池自己执行。
    public static ExecutorService executorService = Executors.newFixedThreadPool(10);
    
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        System.out.println("启动main方法开始");
        
        executorService.submit(new TwoMyThread()); //有返回值
        //每个异步任务,提交给线程池自己执行。
        executorService.execute(new TwoMyThread());//无返回值

        System.out.println("结束main方法");

    }

    /**
     * 实现Runnable接口
     */
    public static class TwoMyThread implements Runnable {

        @Override
        public void run() {
            System.out.println("当前线程:"+Thread.currentThread().getId());
            int i = 10/2;
            System.out.println("运行结果:"+i);
        }
    }
}

运行结果:

启动main方法开始
结束main方法
当前线程:22
运行结果:5

使用线程池带来的好处:

1、降低资源的消耗
通过重复利用已经创建好的线程降低线程的创建和销毁带来的损耗
2、提高响应速度
因为线程池中的线程数没有超过线程池的最大上限时,有的线程处于等待分配任务的状态,当任务来时无需创建新的线程就能执行
3、提高线程的可管理性
线程池会根据当前系统特点对池内的线程进行优化处理,减少创建和销毁线程带来的系统开销。无限的创建和销毁线程不仅消耗系统资源,还降低系统的稳定性,使用线程池进行统一分配

总结区别

1、直接继承Thread、实现Runnable接口:不能直接得到返回值。

2、实现Callable泛型接口:可以得到返回值。

3、继承Thread、实现Runnable接口、实现Callable泛型接口,都不能达到控制资源的效果。

4、只有线程池可以控制资源,性能稳定。

1

评论 (0)

取消