Java 进阶(14) 线程池

概述

我们使⽤线程的时候就去创建⼀个线程,这样实现起来⾮常简便,但是就会有⼀个问题:

如果并发的线程数量很多,并且每个线程都是执⾏⼀个时间很短的任务就结束了,这样频繁创建线程就会⼤⼤降低系统的效率,因为频繁创建线程和销毁线程需要时间。

那么有没有⼀种办法使得线程可以复⽤,就是执⾏完⼀个任务,并不被销毁,⽽是可以继续执⾏其他的任务?

在Java中可以通过线程池来达到这样的效果。

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

合理利用线程池能够带来三个好处:

1. 降低资源消耗。减少了创建和销毁线程的次数,每个⼯作线程都可以被重复利⽤,可执⾏多个任务。

2. 提⾼响应速度。当任务到达时,任务可以不需要的等到线程创建就能⽴即执⾏。

3. 提⾼线程的可管理性。可以根据系统的承受能⼒,调整线程池中⼯作线线程的数⽬,防⽌因为消耗过多的内存,⽽把服务器累趴下(每个线程需要⼤约1MB内存,线程开的越多,消耗的内存也就越⼤,最后死机)。

线程池的使用

Java⾥⾯线程池的顶级接⼝是 java.util.concurrent.Executor ,但是严格意义上讲 Executor

并不是⼀个线程池,⽽只是⼀个执⾏线程的⼯具。真正的线程池接⼝是java.util.concurrent.ExecutorService 。

要配置⼀个线程池是⽐较复杂的,尤其是对于线程池的原理不是很清楚的情况下,很有可能配置的线程池不是较优的,因此在 java.util.concurrent.Executors 线程⼯⼚类⾥⾯提供了⼀些静态⼯⼚,⽣成⼀些常⽤的线程池。官⽅建议使⽤Executors⼯程类来创建线程池对象。

Java类库提供了许多静态⽅法来创建⼀个线程池:

Executors类中创建线程池的⽅法如下:

a、 newFixedThreadPool 创建⼀个固定⻓度的线程池,当到达线程最⼤数量时,线程池的规模将不再变化。

b、 newCachedThreadPool 创建⼀个可缓存的线程池,如果当前线程池的规模超出了处理需求,将回收空的线程;当需求增加时,会增加线程数量;线程池规模⽆限制。

c、 newSingleThreadPoolExecutor 创建⼀个单线程的Executor,确保任务对了,串⾏执⾏

d、 newScheduledThreadPool 创建⼀个固定⻓度的线程池,⽽且以延迟或者定时的⽅式来执⾏,类似Timer;

使⽤线程池中线程对象的步骤:

1. 创建线程池对象。

2. 创建Runnable接⼝⼦类对象。(task)

3. 提交Runnable接⼝⼦类对象。(take task)

获取到了⼀个线程池ExecutorService 对象,定义了⼀个使⽤线程池对象的⽅法如下:

public Future submit(Runnable task) :获取线程池中的某⼀个线程对象,并执⾏Future接⼝:⽤来记录线程任务执⾏完毕后产⽣的结果。线程池创建与使⽤。

4. 关闭线程池(⼀般不做)。

示例:

class MyRunnable 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("教完后,教练回到了游泳池");
    }
}


public class ThreadPoolDemo {
    public static void main(String[] args) {

        //创建线程池对象。
        //创建一个固定长度的线程池,当到达线程最大数量时,线程池的规模将不再变化。
        //ExecutorService pool = Executors.newFixedThreadPool(3);
        //创建一个可缓存的线程池,如果当前线程池的规模超出了处理需求,将回收空的线程;当需求增加时,会增加线程数量
        //ExecutorService pool = Executors.newCachedThreadPool();
        //创建一个单线程的Executor
        //ExecutorService pool = Executors.newSingleThreadExecutor();

        //创建Runnable接口子类对象。(task)
        MyRunnable myRunnable = new MyRunnable();
//        //提交Runnable接口子类对象。(take task)
//        pool.submit(myRunnable);
//        pool.submit(myRunnable);
//        pool.submit(myRunnable);
//        //注意:submit方法调用后,程序并不终止,因为线程池控制了线程的关闭
//
//
//        //关闭线程池(一般不做)。
//        pool.shutdown();


        //
        //new Thread(myRunnable,"线程1").start();


        //创建一个固定长度的线程池,而且以延迟或者定时的方式来执行,类似Timer
        ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(3);
        for (int i = 0; i < 3; i++) {
            scheduledExecutorService.schedule(myRunnable,3, TimeUnit.SECONDS);//每个任务延迟10秒执行
        }

        scheduledExecutorService.shutdown();//关闭线程池,不会马上终止程序
        while(!scheduledExecutorService.isTerminated()){

        }

        System.out.println("程序终止!!");
    }
}

Callable接⼝

⼀般情况下,使⽤Runnable接⼝、Thread实现的线程我们都是⽆法返回结果的。但是如果对⼀些场合需要线程返回的结果。就要使⽤⽤Callable、Future这⼏个类。Callable只能在ExecutorService的线程池中跑,但有返回结果,也可以通过返回的Future对象查询执⾏状态。

Future 本身也是⼀种设计模式,它是⽤来取得异步任务的结果。

看看其源码:

public interface Callable {
    V call() throws Exception;
}

它只有⼀个call⽅法,并且有⼀个返回V,是泛型。可以认为这⾥返回V就是线程返回的结果。

ExecutorService接⼝:线程池执⾏调度框架

 Future submit(Callable task);
 Future submit(Runnable task, T result);
Future submit(Runnable task);

示例:

import java.util.concurrent.*;

class HandleCallable implements Callable{

    private String name;
    public HandleCallable(String name){
        this.name = name;
    }

    @Override
    public Integer call() throws Exception {
        System.out.println(this.name+"开始计算了...");
        //睡眠
        Thread.sleep(200);

        int sum = 0;
        //产生一个随机数
        int random = (int)(Math.random()*10);//Math.random()--->0~1
        System.out.println(this.name+"产生的随机数:"+random);
        for (int i = 0; i <= random; i++) {
            sum  +=i;
        }
        return sum;
    }
}

public class FutureTest {
    public static void main(String[] args) {

        //线程池
        ExecutorService pool = Executors.newFixedThreadPool(3);
        HandleCallable t1 = new HandleCallable("线程1");
        HandleCallable t2 = new HandleCallable("线程2");
        HandleCallable t3 = new HandleCallable("线程3");
        //提交任务
        Future result1 = pool.submit(t1);
        Future result2 = pool.submit(t2);
        Future result3 = pool.submit(t3);

        //关闭线程池
        pool.shutdown();

        //将结果打印一下
        try {
            System.out.println("线程1的结果:"+result1.get());
            System.out.println("线程2的结果:"+result2.get());
            System.out.println("线程3的结果:"+result3.get());
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
    }
}

你可能感兴趣的:(Java,进阶,java,jvm,开发语言)