线程池

1.线程池的优点

Java中的线程池是运用场景最多的并发框架,几乎所有需要异步或者并发执行任务的程序都可以使用线程池。使用线程池的优点如下:

(1)降低资源消耗

通过重复利用已创建的线程降低线程创建与消耗带来的损耗

(2)提高响应速度

当任务到达时,无须等待线程创建就可以立即执行。

(3)提高线程的可管理性

使用线程池可以统一进行线程分配、调度与调控。

2.线程池的继承关系

ExecutorService(普通调度池核心接口)
     submit(Callable|Runnable):Future<T>

ScheduledExecutorService(定时调度池核心接口)
    schedule(Runnable|Callable command,long delay,TimeUnit unit):延迟delay个时间单位后开始执行。
    scheduledAtFixedRate(Runnable command,long initialDelay,long period,TimeUnit unit):延迟initialDelay个时间单位后开始执行,并且每隔period个时间单位就执行一次。

3.线程池执行流程(*****)

第一步:判断核心线程池是否已满,如果未满,创建一个新的线程来执行任务。如果已满,判断是否有空闲线程,有的话,将任务分配给空闲线程,否则,执行步骤2.(创建线程需要获得全局锁)

第二步:判断工作队列(阻塞队列BlockingQueue)是否已满。如果工作队列未满,将任务存储在工作队列中等待空闲线程调度。如果工作队列已满,执行步骤3.

第三步:判断当前线程池的线程数量是否已达到最大值,如果没有达到最大值,则创建新的线程来执行任务(创建线程需要获取全局锁)。否则,执行步骤4.

第四步:调用饱和策略来处理此任务。

4.线程池的使用

4.1 手工创建线程池(*****)

通过new一个ThreadPoolExecutor()就可以实现自定义线程池。

    public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory,
                              RejectedExecutionHandler handler)

参数说明:

1)corePoolSize(核心线程池大小):当提交一个任务到线程池时,只要核心线程池未达到corePoolSize,创建新线程来执行任务。调用preStartAllCoreThreads(),线程池会提前创建并启动所有核心线程。

2)BlockingQueue<Runnable> workQueue(任务队列):保存等待执行任务的阻塞队列。

阻塞队列:

-ArrayBlockingQueue:基于数组有界阻塞队列,按照FIFO原则对元素进行排序。

-LinkedBlockingQueue:基于链表的阻塞队列,按照FIFO排列元素。吞吐量要高于ArrayBlockingQueue。内置线程池newFixedThreadPool-固定大小线程池就采用此队列。

-SynchronousQueue:一个不存储元素的阻塞队列(无界队列)。每个插入操作需要等待另一个线程的移除操作,否则插入操作一直处于阻塞状态。吞吐量通常高于LinkedBlockingQueue。内置线程newCachedThreadPool-缓存线程池就采用此队列。

-PriorityBlockingQueue:具有优先级的阻塞队列。

3)maximumPoolSize(线程池最大数量)

4)keepAliveTime(线程保持活动的时间):线程池的工作线程空闲后,保持存活的时间。如果任务比较多并且每个任务执行的时间比较短,那么可以调大此参数来提高线程利用率。

5)RejectedExecutionHandler(饱和策略)

-AbortPolicy:无法处理新任务抛出异常(JDK默认采用此策略)。

-CallerRunsPolicy:使用调用者所在线程来处理任务。

-DiscardOldestPolicy:丢弃队列中最近的一个任务,并执行当前任务。

-DiscardPolicy:不处理任务,丢弃任务,也不报异常。

6)TimeUnit unit:时间单位

4.2 向线程池提交任务

使用两个方法向线程池提交任务,分别为execute()和submit()。

execute()用于提交不需要返回值的方法,所以无法判断线程池是否执行任务成功。

创建无返回值的线程池

import java.util.concurrent.ExecutorService;
import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

class MyThread implements Runnable{

    @Override
    public void run() {
        for(int i=0;i<10;i++){
            System.out.println(Thread.currentThread().getName()+"、"+i);
        }
    }
}
public class Demo {
    public static void main(String[] args) {
        MyThread myThread=new MyThread();
        ExecutorService executorService=new ThreadPoolExecutor(3,5,2000,TimeUnit.MILLISECONDS,new LinkedBlockingDeque<>());
        for(int i=0;i<5;i++){
            executorService.execute(myThread);
        }
    }
}
//这三个线程很快就执行完,不会达到最大线程池数量。

submit()用于提交需要返回值的任务。线程池会返回一个future类型的对象,通过这个future对象可以判定任务是否执行成功,并且可以通过future的get()来获取返回值,get()会阻塞当前线程直到任务完成,而使用get(long timeout,TimeUnit unit)方法会阻塞当前线程一段时间之后立即返回,此时有可能任务没有执行完。

创建有返回值的线程池

import java.util.concurrent.*;

class MyThread implements Callable {

    @Override
    public String call() {
        for(int i=0;i<10;i++){
            System.out.println(Thread.currentThread().getName()+"、"+i);
        }
        return Thread.currentThread().getName()+"执行完毕";
    }
}
public class Demo {
    public static void main(String[] args) {
        MyThread myThread=new MyThread();
        ExecutorService executorService=new ThreadPoolExecutor(3,5,2000,TimeUnit.MILLISECONDS,new LinkedBlockingDeque<>());
    //调用future的get(),会阻塞其它线程,直到取得当前线程执行完毕后的返回值。
        //FutureTask中的任务只会被执行一次。
        for(int i=0;i<5;i++){
            Future future=executorService.submit(myThread);
            try {
                System.out.println(future.get());
            } catch (InterruptedException e) {
                e.printStackTrace();
            } catch (ExecutionException e) {
                e.printStackTrace();
            }
        }
        executorService.shutdown();//关闭线程池
    }
}

4.3 关闭线程池

通过调用线程池的shutdown或shutdownNow方法来关闭线程池。它们的原理是遍历线程池中的工作线程,然后逐个调用线程的interrupt()来中断线程,所以无法响应中断的任务可能永远无法终止。但是它们有区别shutdown():安全关闭  shutdownNow():立即关闭,具体区别如下:

shutdownNow首先将线程池的状态设置为STOP,然后尝试停止所有的正在执行或暂停任务的线程,并返回等待执行任务的列表。

shutdown只是将线程池的状态设置成SHUTDOWN状态,然后中断所有没有正在执行任务的线程。

只要调用了这两个关闭方法中的任意一个,isShutdown方法就会返回true。当所有的任务都关闭之后,才表示线程池关闭成功,这时调用isTerminaed方法会返回true。至于调用哪个方法来关闭线程池,应该由提交到线程池的任务特性决定,通常调用shutdown方法来关闭线程池。如果任务不一定要执行完,则可以调用shutdownNow()。

5.内置四大线程池

5.1 固定大小线程池

public static ExecutorService newFixedThreadPool(int nThreads) {
        return new ThreadPoolExecutor(nThreads, nThreads,
                                      0L, TimeUnit.MILLISECONDS,
                                      new LinkedBlockingQueue<Runnable>());
    }

应用场景:固定大小线程池适用于为了满足资源管理需求,而需要限制当前线程数量的应用场合,适用于负载较重的服务器。

5.2 单线程池newSingleThreadExecutor

    public static ExecutorService newSingleThreadExecutor() {
        return new FinalizableDelegatedExecutorService
            (new ThreadPoolExecutor(1, 1,
                                    0L, TimeUnit.MILLISECONDS,
                                    new LinkedBlockingQueue<Runnable>()));
    }

应用场景:适用于需要保证顺序地执行各个任务,并且在任意时间不会有多个线程。

5.3 缓存线程池CachedThreadPool

根据需要创建新线程。
    public static ExecutorService newCachedThreadPool() {
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                      60L, TimeUnit.SECONDS,
                                      new SynchronousQueue<Runnable>());
    }

当提交任务速度快于执行任务速度,缓存线程池会不断创建新线程;如果提交任务速度大于执行任务速度,只创建一个线程。
适用场景:大小无界线程池,适用于执行很多短期异步小程序,或者负载较轻的服务器。

import java.util.concurrent.*;

class MyThread implements Callable {

    @Override
    public String call() {
        for(int i=0;i<10;i++){
            System.out.println(Thread.currentThread().getName()+"、"+i);
        }
        return Thread.currentThread().getName()+"执行完毕";
    }
}
public class Demo {
    public static void main(String[] args) {
        MyThread myThread=new MyThread();
       ExecutorService executorService=Executors.newCachedThreadPool();
       for(int i=0;i<5;i++){
           executorService.submit(myThread);
       }
       executorService.shutdown();
    }
}

使用该线程池会创建5个线程。

5.4 定时调度池newScheduledThreadPool(int corePoolSize)

public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
        return new ScheduledThreadPoolExecutor(corePoolSize);
    }

应用场景:需要定时执行任务的应用场景。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值