Java并发34:Callable+Future系列--Callable接口学习笔记

本章主要学习Callable接口。

1.Callable接口概述

Callable接口是一种能够返回计算结果并且可以抛出异常的任务
Callable接口的实现类需要定义一个无参数的方法: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;
}

Callable接口与Runnable接口类似,都是为了实现多线程而设计的。
但是,Runnable接口并不能返回结果,也不能抛出一个可检查的异常。

Executors工具类包含一些工具方法,这些方法能够将一些其他常见的形式转换成Callable接口的实现类。
关于这些转换方法,可以参考后续文章。

2.Callable与Runnable对比

类似之处:

  • 都是为了实现多线程而设计的
  • 都可以在Executor框架中使用

区别之处:

  • Callable接口可以返回结果;Runnable接口不能返回结果
  • Callable接口可以抛出异常;Runnable接口不能抛出异常
  • Callable接口是泛型接口;Runnable接口是普通接口
  • Callable接口常常与Future结合使用

3.实例练习

练习目的:

  • 加深对Callable接口和Runnable接口的理解。
  • 了解两种多线程编程方式的区别:Thread和Executor。
  • 了解Callable 和Future 的基本用法,理解返回结果的优势。

练习说明:

  • 分别以下面三种方式实现随机数的获取:
  • 1.Runnable + Thread
  • 2.Runnable + Executor
  • 3.Callable + Future + Executor

实例代码:

Runnable编程方式:

/**
 * 多线程示例-获取随机值-实现Runnable接口
 */
//定义一个共享变量
static AtomicInteger value = new AtomicInteger(0);

//实现Runnable
static class RandomByRunnable implements Runnable{
    @Override
    public void run() {
        //模拟计算
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        //CAS赋值
        value.compareAndSet(0,RandomUtils.nextInt(100,200));
    }
}

Callable编程方式:

/**
 * 多线程示例-获取随机值-实现Callable接口
 */
static class RandomByCallable implements Callable<Integer>{

    @Override
    public Integer call() throws Exception {
        Thread.sleep(1000);
        return RandomUtils.nextInt(100,200);
    }
}

三种多线程方式:



import java.util.Random;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.atomic.AtomicInteger;

public class Thread34 {
static AtomicInteger value = new AtomicInteger(0);
public static void main(String[] args) throws Exception{
//实现Runnable接口的方式
new Thread(new RandomByRunnable()).start();
//通过while去循环查询是否获取了值
while (value.get() == 0);
System.out.println("实现Runnable接口的线程(裸线程),获取了结果:" + value.get());

//实现Runnable接口 + Executor框架的方式
ExecutorService executorService = Executors.newCachedThreadPool();
//重新初始化
value = new AtomicInteger(0);
executorService.submit(new RandomByRunnable());
//通过while去循环查询是否获取了值
while (value.get() == 0);
System.out.println("实现Runnable接口的线程 + Executor框架,获取了结果:" + value.get());

//实现Callable接口 + Executor框架
Future<Integer> result = executorService.submit(new RandomByCallable());
System.out.println("实现Callable接口 + Executor框架,获取了结果:" + result.get());
executorService.shutdown();


}


static class RandomByRunnable implements Runnable{
@Override
public void run() {
//模拟计算
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
Random random=new Random();
//CAS赋值
value.compareAndSet(0,random.nextInt(200));
}
}

/**
* 多线程示例-获取随机值-实现Callable接口
*/
static class RandomByCallable implements Callable<Integer> {

@Override
public Integer call() throws Exception {
Thread.sleep(1000);
Random random=new Random();
return random.nextInt(200);
}
}
}

线程池不允许使用Executors去创建,而是通过ThreadPoolExecutor的方式,这样的处理方式让写的同学更加明确线程池的运行规则,规避资源耗尽的风险。 说明:Executors返回的线程池对象的弊端如下:
1)FixedThreadPool和SingleThreadPool:
  允许的请求队列长度为Integer.MAX_VALUE,可能会堆积大量的请求,从而导致OOM。
2)CachedThreadPool:
  允许的创建线程数量为Integer.MAX_VALUE,可能会创建大量的线程,从而导致OOM。
            
Positive example 1//org.apache.commons.lang3.concurrent.BasicThreadFactory
    ScheduledExecutorService executorService = new ScheduledThreadPoolExecutor(1,
        new BasicThreadFactory.Builder().namingPattern("example-schedule-pool-%d").daemon(true).build());
       
        
            
Positive example 2:
    ThreadFactory namedThreadFactory = new ThreadFactoryBuilder()
        .setNameFormat("demo-pool-%d").build();

    //Common Thread Pool
    ExecutorService pool = new ThreadPoolExecutor(5, 200,
        0L, TimeUnit.MILLISECONDS,
        new LinkedBlockingQueue<Runnable>(1024), namedThreadFactory, new ThreadPoolExecutor.AbortPolicy());

    pool.execute(()-> System.out.println(Thread.currentThread().getName()));
    pool.shutdown();//gracefully shutdown
       
        
            
Positive example 3<bean id="userThreadPool"
        class="org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor">
        <property name="corePoolSize" value="10" />
        <property name="maxPoolSize" value="100" />
        <property name="queueCapacity" value="2000" />

    <property name="threadFactory" value= threadFactory />
        <property name="rejectedExecutionHandler">
            <ref local="rejectedExecutionHandler" />
        </property>
    </bean>
    //in code
    userThreadPool.execute(thread);c

 

运行结果:

实现Runnable接口的线程(裸线程),获取了结果:175
实现Runnable接口的线程 + Executor框架,获取了结果:163
实现Callable接口 + Executor框架,获取了结果:162

 

posted @ 2021-09-02 19:29  姚春辉  阅读(60)  评论(0编辑  收藏  举报