Java 多线程三种实现

人走茶凉 提交于 2020-11-30 23:41:34

纵览三种实现方式:

  • 实现Runnable接口
  • 继承Thread类
  • 实现Callable<V>接口,结合FutureTask<V>

文末附加线程池操作。 

一、实现Runnable接口

public class MyRunnable implements Runnable {

    @Override
    public void run() {
        System.out.println(Thread.currentThread().getId() + " Hello Runnable!");
    }

    public static void main(String[] args) throws Exception {
        // 一个线程哪够,来仨(sa)...
        new Thread(new MyRunnable()).start();
        new Thread(new MyRunnable()).start();
        new Thread(new MyRunnable()).start();
    }
}

如上类图,MyRunnable实现了Runnable接口。其中Runnable由@FunctionalInterface注解标注,也就是可以用函数式编程的方式简写,简写后如下:

new Thread(() -> System.out.println(Thread.currentThread().getId() + " Hello Runnable!")).start();

二、继承Thread类

public class MyThread extends Thread {

    @Override
    public void run() {
        System.out.println(Thread.currentThread().getId() + " Hello Thread!");
    }

    public static void main(String[] args) {
        new MyThread().start();
        new MyThread().start();
        new MyThread().start();
    }
}

这个很简单,Thread实现了Runnable接口,以继承的方式实现,在java里边不像接口那么好用,毕竟类是单继承的。

三、实现Callable<V>接口,结合FutureTask<V>

public class MyCallable implements Callable<String> {

    @Override
    public String call() throws Exception {
        return Thread.currentThread().getId() + " Hello Callable!";
    }

    public static void main(String[] args) throws Exception {
        for (int i = 0; i < 3; i++) {
            Callable<String> callable = new MyCallable();
            FutureTask<String> task = new FutureTask<>(callable);
            new Thread(task).start();
            // 获取异步线程返回的数据
            System.out.println(task.get());
        }
    }

}

 

结合如上类图,以及Callable源码,它的源码很简单,只定义了一个带返回值的call方法,和Runnable完全没关系,所以说直接用Callable不能实现多线程的。

此时引入了另一个类FutureTask,它的构造方法中可以接收Callable,再看它的类图,间接实现了Runnable接口,这下豁然开朗。

Callable也由@FunctionalInterface注解标注,所以也能简写,如下:

Callable<String> callable = () -> Thread.currentThread().getId() + " Hello Callable!";
// 下边的和之前一样
FutureTask<String> futureTask = new FutureTask<>(callable);
new Thread(futureTask).start();
System.out.println(futureTask.get());

四、附加线程池操作

IDEA安装了阿里编码规范插件,new Thread(...) ,总会警告,索性用线程池好了。

// 创建线程工厂,并设置线程名字格式
ThreadFactory threadFactory = new ThreadFactoryBuilder().setNameFormat("thread-pool-%d").build();

ExecutorService singleThreadPool = new ThreadPoolExecutor(12, 24,
        0L, TimeUnit.MILLISECONDS,
        new LinkedBlockingQueue<>(1024), threadFactory, new ThreadPoolExecutor.AbortPolicy());
for (int i = 0; i < 12; i++) {
    singleThreadPool.execute(() -> System.out.println(Thread.currentThread().getName()));
}

singleThreadPool.shutdown();
  1. 其中ThreadFactoryBulider用的是Google的一个java开源库
    <dependency>
        <groupId>com.google.guava</groupId>
        <artifactId>guava</artifactId>
        <version>23.6-jre</version>
    </dependency>
  2. ThreadPoolExecutor构造参数
    • ThreadPoolExecutor(int corePoolSize,
                                    int maximumPoolSize,
                                    long keepAliveTime,
                                    TimeUnit unit,
                                    BlockingQueue<Runnable> workQueue,
                                    ThreadFactory threadFactory,
                                    RejectedExecutionHandler handler)
    • corePoolSize:核心线程数;
    • maximumPoolSize:线程池所能容纳的最大线程数;
    • keepAliveTime:非核心线程的闲置超时时间,超过这个时间就会被回收;
    • unit:指定keepAliveTime的单位,如 TimeUnit.SECONDS;
    • workQueue:线程池中的任务队列;通俗说就是线程超出核心线程数量后,线程怎么继续执行的策略;
    • threadFactory:线程工厂,提供创建新线程的功能;
    • handler:线程池对拒绝任务的策略;ThreadPoolExecutor里面定义了 4 种 handler 策略:
      • CallerRunsPolicy :这个策略重试添加当前的任务,他会自动重复调用 execute() 方法,直到成功。
      • AbortPolicy :对拒绝任务抛弃处理,并且抛出异常。
      • DiscardPolicy :对拒绝任务直接无声抛弃,没有异常信息。
      • DiscardOldestPolicy :对拒绝任务不抛弃,而是抛弃队列里面等待最久的一个线程,然后把拒绝任务加到队列。
  3. ExecutorService 的 execute方法接受参数为Runnable,所以上边用到的Thread、FutureTask等均可。

完。。。

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!