线程池

一. 概念

线程池,其实就是一个容纳多个线程的容器,其中的线程可以反复使用,省去了频繁创建线程对象的操作,无需反复创建线程而消耗过多资源。

二. 为什么使用线程池

在java中,如果每个请求到达就创建一个新线程,开销是相当大的。在实际使用中,创建和销毁线程花费的时间和消耗的系统资源都相当大,甚至可能要比在处理实际的用户请求的时间和资源要多的多。除了创建和销毁线程的开销之外,活动的线程也需要消耗系统资源。如果在一个jvm里创建太多的线程,可能会使系统由于过度消耗内存或“切换过度”而导致系统资源不足。为了防止资源不足,需要采取一些办法来限制任何给定时刻处理的请求数目,尽可能减少创建和销毁线程的次数,特别是一些资源耗费比较大的线程的创建和销毁,尽量利用已有对象来进行服务。

线程池主要用来解决线程生命周期开销问题和资源不足问题。通过对多个任务重复使用线程,线程创建的开销就被分摊到了多个任务上了,而且由于在请求到达时线程已经存在,所以消除了线程创建所带来的延迟。这样,就可以立即为请求服务,使用应用程序响应更快。另外,通过适当的调整线程中的线程数目可以防止出现资源不足的情况。

三. 线程池 — Runnable接口(没有返回值,不可抛出异常)

// 实现的Runnable接口
public class ThreadPoolServiceImpl implements Runnable {
    @Override
    public void run() {
        System.out.println("我要一个教练");
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("教练来了: " + Thread.currentThread().getName());
        System.out.println("教我游泳,交完后,教练回到了游泳池");
    }
}

// main调用
public static void main(String[] args) {
    //创建线程池对象
    ExecutorService service = Executors.newFixedThreadPool(2);//包含2个线程对象
    //创建Runnable实例对象
    ThreadPoolServiceImpl r = new ThreadPoolServiceImpl();
    //从线程池中获取线程对象,然后调用ThreadPoolServiceImpl中的run()
    service.submit(r);
    //再获取个线程对象,调用MyRunnable中的run()
    service.submit(r);
    service.submit(r);
    //注意:submit方法调用结束后,程序并不终止,是因为线程池控制了线程的关闭。将使用完的线程又归还到了线程池中
    //关闭线程池
    service.shutdown();
}

四. 线程池 — Callable接口(有返回值,可抛出异常)

// 实现的Callable接口
public class ThreadPoolCallableServiceImpl implements Callable {
    @Override
    public Object call() throws Exception {
        System.out.println("我要一个教练:call");
        Thread.sleep(2000);
        System.out.println("教练来了: " +Thread.currentThread().getName());
        System.out.println("教我游泳,交完后,教练回到了游泳池");
        return null;
    }
}

// main调用
public static void main(String[] args) {
    //创建线程池对象
    ExecutorService service = Executors.newFixedThreadPool(2);//包含2个线程对象
    //创建Runnable实例对象
    ThreadPoolCallableServiceImpl r = new ThreadPoolCallableServiceImpl();
    //从线程池中获取线程对象,然后调用ThreadPoolCallableServiceImpl中的run()
    service.submit(r);
    //再获取个线程对象,调用MyRunnable中的run()
    service.submit(r);
    service.submit(r);
    //注意:submit方法调用结束后,程序并不终止,是因为线程池控制了线程的关闭。将使用完的线程又归还到了线程池中
    //关闭线程池
    service.shutdown();
}

五. 练习案例 — Callable接口做计算

// 实现Callable的接口
public class ThreadPoolCallableServiceImpl implements Callable<Integer> {
    int x= 1;
    int y=1;

    public ThreadPoolCallableServiceImpl(Integer x,Integer y){
        this.x = x;
        this.y = y;
    }

    @Override
    public Integer call() throws Exception {
        return x+y;
    }
}

// main测试
public static void main(String[] args) throws InterruptedException, ExecutionException {
    //创建线程池对象
    ExecutorService service = Executors.newFixedThreadPool(2);//包含2个线程对象
    //创建Runnable实例对象
    ThreadPoolCallableServiceImpl r1 = new ThreadPoolCallableServiceImpl(100, 200);
    ThreadPoolCallableServiceImpl r2 = new ThreadPoolCallableServiceImpl(200, 2);

    //从线程池中获取线程对象,然后调用ThreadPoolServiceImpl中的run()
    Future<Integer> submit1 = service.submit(r1);
    System.out.println("submit1:" + submit1.get());
    Future<Integer> submit2 = service.submit(r2);
    System.out.println("submit2:" + submit2.get());
    //注意:submit方法调用结束后,程序并不终止,是因为线程池控制了线程的关闭。将使用完的线程又归还到了线程池中
    //关闭线程池
    service.shutdown();
}