多线程之线程池

1、线程池是什么

线程池是一种多线程处理形式,处理过程中将任务添加到队列,然后在创建线程后自动启动这些任务。线程池线程都是后台线程。每个线程都使用默认的堆栈大小,以默认的优先级运行,并处于多线程单元中。如果某个线程在托管代码中空闲(如正在等待某个事件),则线程池将插入另一个辅助线程来使所有处理器保持繁忙。如果所有线程池线程都始终保持繁忙,但队列中包含挂起的工作,则线程池将在一段时间后创建另一个辅助线程但线程的数目永远不会超过最大值。超过最大值的线程可以排队,但他们要等到其他线程完成后才启动。

2、线程池的优势

降低系统资源消耗,通过重用已存在的线程,降低线程创建和销毁造成的消耗

提高系统响应速度,当有任务到达时,通过复用已存在的线程,无需等待新线程的创建便能立即执行

方便线程并发数的管控。因为线程若是无限制的创建,可能会导致内存占用过多而产生OOM,并且会造成cpu过度切换(cpu切换线程是有时间成本的(需要保持当前执行线程的现场,并恢复要执行线程的现场))

提供更强大的功能,延时定时线程池

3、创建线程池

(1)Executors创建线程池

/**
     * 创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程 队列无限长
     */
    @Test
    public void Test01() {
        ExecutorService newCachedThreadPool = Executors.newCachedThreadPool();
        for (int i = 0; i < 100; i++) {
            newCachedThreadPool.execute(new Runnable() {
                public void run() {
                    System.out.println(Thread.currentThread().getName());
                }
            });
        }
    }
/**
     * 创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待
     */
    @Test
    public void Test02() {
        ExecutorService newFixedThreadPool = Executors.newFixedThreadPool(3);
        for (int i = 0; i < 100; i++) {
            newFixedThreadPool.execute(new Runnable() {
                public void run() {
                    System.out.println(Thread.currentThread().getName());
                }
            });
        }
    }
/**
     * 创建一个周期线程池,支持定时及周期性任务执行 队列无限长
     */
    @Test
    public void Test03() {
        ScheduledExecutorService newScheduledThreadPool = Executors.newScheduledThreadPool(3);
        for (int i = 0; i < 100; i++) {
            newScheduledThreadPool.schedule(new Runnable() {
                public void run() {
                    System.out.println(Thread.currentThread().getName());
                }
            }, 3, TimeUnit.SECONDS);
        }
    }
/**
     * 创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行
     */
    @Test
    public void Test04() {
        ExecutorService newSingleThreadExecutor = Executors.newSingleThreadExecutor();
        for (int i = 0; i < 100; i++) {
            newSingleThreadExecutor.execute(new Runnable() {
                public void run() {
                    System.out.println(Thread.currentThread().getName());
                }
            });
        }
    }

注:不推荐使用Executors创建线程池,因为看源码队列是无限长也就是无界队列消耗资源,高并发下很快会崩。

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

 

4、自定义线程

我们使用ThreadPoolExecutor来自定义多线程

public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue) {
    this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
         Executors.defaultThreadFactory(), defaultHandler);
}
corePoolSize :线程池常驻核心线程数。创建线程池后,当有请求任务来之后,就会安排池中线程去执行请求任务,近似理解为今日当值线程。当线程池中的线程数目                            达到了corePoolSize后,就会把任务放到缓存队列中;
maxmumPoolSize:线程池能够容纳同时执行的最大线程数,此值必须大于等于1
keepAliveTime:多余空闲线程的存活时间。当前线程池的数量超过corePoolSize时,当空闲时间达到keepAliveTime值时,多余空闲线程会被销毁直到只剩下                                             corePoolSize个线程为止。
unit:keepAliveTime的时间单位
workQueue:任务队列,被提交但未被执行的任务
threadFactory:表示生成线程池中线程的线程工厂,用于创建线程,一般用默认的即可
handler:拒绝策略,表示当队列满了并且工作线程大于等于线程池的最大线程数(maxmumPoolSize)
5、阻塞队列与非阻塞队列
  阻塞队列:在队列为空时,获取元素的线程会等待队列变为非空;
                    在队列为满时,存储元素的线程会等待队列可用,也就是等待去他线程出队
一个线程从一个空的阻塞队列中取元素,此时线程会被阻塞直到阻塞队列中有了元素。当队列中有元素后,被阻塞的线程会自动被唤醒(不需要我们编写代码去唤醒)。这样提供了极大的方便性。如果使用非阻塞队列,它不会对当前线程产生阻塞,就必须额外地实现同步策略以及线程间唤醒策略,这个实现起来就非常麻烦。
6、线程池中的队列为阻塞队列
 

 

posted @ 2020-04-11 22:13  叔叔好人呐  阅读(230)  评论(0编辑  收藏  举报