Java线程池

2020-08-07  本文已影响0人  小李同学今天博学了吗
第一个问题我们为什么要用线程池

1.在Android中我们做一个超时操作的时候,不能在主线程执行,这是就会创建许多线程去执行,而创建的线程我们是无法管理的,这样会造成线程之间相互竞争而增加系统消耗,之后我们便想到了线程池来对他们进行统一管理

为什么Android中要用UI线程来更新UI?
因为主线程又称为UI线程,主要工作是负责UI界面的更新,如果在主线程执行就有可能造成主线程的阻塞,导致ANR(为什么是主线程更新UI,而其他线程不可以,主要是因为更新UI的方法并不是线程安全的,也就是说如果有两个子线程同时去更新UI,那这是的UI就会出错)

线程池可以做什么?

1.通过对线程的统一管理,重用已存在的线程,降低线程的创建和销毁的消耗,降低系统资源的消耗
2.提高系统的响应速度,可以直接重用已经存在的线程,减少了线程创建的时候
3.对线程并发数的管控,防止线程创建过多而消耗资源阻塞系统
4.线程池提供了定时、定期以及可控线程数的功能

如何使用他?
public ThreadPoolExecutor(int corePoolSize,
    int maximumPoolSize,
    long keepAliveTime,
    TimeUnit unit,
    BlockingQueue<Runnable> workQueue,
    ThreadFactory threadFactory,
    RejectedExecutionHandler handler)

我们先看一下他的构造方法
1.corePoolSize:为核心线程数,表示允许线程池中允许同时运行的最大线程数
2.maximumPoolSize:线程池允许创建的最大线程数,他>= 核心线程数
3.keepAliveTime:即为线程的存活时间,当有所创建的线程数大约corePoolSize,且有线程闲置达到存活时间,这个时候就会执行shutDown
4.unit为keepAliveTime的时间单位,是一个枚举类型,有小时、分钟、秒等可选。
5.workQueue:为阻塞队列,当线程数超过了corePoolSize是就会添加到阻塞队列,如何阻塞队列已经满了,那么就加不进去,就会创建一个新的线程来执行任务,如果线程的总数超过了maximumPoolSize,就会采用拒绝策略
阻塞队列类型:
ArrayBlockingQueue
基于数组实现的有界的阻塞队列,该队列按照 FIFO(先进先出)原则对队列中的元素进行排序。
LinkedBlockingQueue
基于链表实现的阻塞队列,该队列按照FIFO(先进先 出)原则对队列中的元素进行排序。
SynchronousQueue
内部没有任何容量的阻塞队列。在它内部没有任何的 缓存空间。对于SynchronousQueue中的数据元素只 有当我们试着取走的时候才可能存在。
PriorityBlockingQueue 具有优先级的无限阻塞队列。
6.threadFactory:线程池工厂:为线程池天宫线程的创建
7.handler :这里表示当队列已满且线程数达到最大线程数时的拒绝策略,默认为AbortPolicy
可选的有
a.CallerRunsPolicy:只有调用者所在线程来执行任务
b.AbortPolicy:直接抛出RejectExecutionException异常
c.DiscardPolicy:丢弃掉该任务,不进行处理
d.DiscardOldestPolicy:丢弃队列里最近的一个任务,并执行当前任务

ExecutorService service = new ThreadPoolExecutor(2,2,5,TimeUnit.HOURS,new LinkedBlockingQueue<>());
        Thread thread = new Thread(new Runnable() {

            @Override
            public void run() {
                // TODO Auto-generated method stub
                for(int i  = 0;i < 10;i++) {
                    System.out.println("第"+i+"条");
                }
            }
            
        }) ;
        service.execute(thread);

在上面这段代码中我们的线程工厂采取默认的DefaultThreadFactory,RejectedExecutionHandler都是采取默认的abortPolicy

线程池的开启方式: 1. execute() ,通过这个方法来提交任务,没有返回值,我们无法判断任务是否被线程池执行

  1. submit,这个方法会返回一个future,通过future可以判断当前任务是否执行成功,通过get方法取得返回的值
Future<Integer> future = service.submit(new Callable<Integer>() {
    @Override
    public Integer call() throws Exception {
      System.out.println("submit方式");
      return 2; 
        }
});
   try {
        Integer number = future.get(); } catch (ExecutionException e) {
// TODO Auto-generated catch block
e.printStackTrace(); 
}

线程池的关闭:1.ShutDown即为当所有在执行的线程都执行完后关闭线程池,
2.shutDownNow为立即关闭,即中断所有线程)

线程池的种类

1.newFixedThreadPool

 ExecutorService service = Executors.newFixedThreadPool(4);


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

通过构造方法我们可以得出他只有核心线程,没有超时限制,即线程池如果处于空闲状态也不会回收,采用LinkedBlockingQueue请求队列没有数量限制

2.newCachedThreadPool

public static ExecutorService newCachedThreadPool() { 
    return new ThreadPoolExecutor(0, Integer.MAX_VALUE,60L, TimeUnit.SECONDS,
        new SynchronousQueue<Runnable>());
}

没有核心线程、线程数量为Integer.MAX_VALUE、超时时间为60s,任务队列采用SynchronouQueue,即队列中不存在任务,即如果当前线程无法接收任务,就创建一个线程去执行该任务

3.newScheduledThreadPool

public static ScheduledExecutorService newScheduledThreadPool(int
 corePoolSize) {
    return new ScheduledThreadPoolExecutor(corePoolSize);
}
public ScheduledThreadPoolExecutor(int corePoolSize) { 
  super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
}

ScheduledExecutorService service = Executors.newScheduledThreadPool(4);
        service.schedule(new Runnable() {
        public void run() { 
            System.out.println(Thread.currentThread().getName()+"延迟三秒执行"); 
        }
        
        }, 3, TimeUnit.SECONDS); 
        service.scheduleAtFixedRate(new Runnable() {
        public void run() {
            System.out.println(Thread.currentThread().getName()+"延迟三秒后每隔2秒执行"); 
        
        }
        }, 3, 2, TimeUnit.SECONDS);

核心数固定、线程数量为Integer.MAX_VALUE,超时时间为0,即闲置就回收,
启动这个线程池有四种方法

3.1.schedule(Runnable command, long delay, TimeUnit unit) :延迟一定时间 后执行Runnable任务;
3.2.schedule(Callable callable, long delay, TimeUnit unit) :延迟一定时 间后执行Callable任务;
3.3. scheduleAtFixedRate(Runnable command, long initialDelay, longperiod, TimeUnit unit),延迟一定时间后,以间隔period时间的频率周期性地
执行任务;
3.4. scheduleWithFixedDelay(Runnable command, long initialDelay, long delay,TimeUnit unit)
与scheduleAtFixedRate()方法很类似,但是不同的是 scheduleWithFixedDelay()方法的周期时间间隔是以上一个任务执行结束到下一个任务开始执行的间隔,而scheduleAtFixedRate()方法的周期时间间隔是以上一个任 务开始执行到下一个任务开始执行的间隔,也就是这一些任务系列的触发时间都是可预知的。

4.newSingleThreadExecutor

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

即只有一个核心线程,限制即回收,请求任务队列是无限的

上一篇下一篇

猜你喜欢

热点阅读