谈谈对线程池的理解

1.什么是线程池?

线程池就是事先将多个线程对象放入一个容器中,当需要使用时,不需要new 线程,直接去线程池拿线程就行了。

2.为什么使用线程池?

1.节省开辟子线程的时间,提高代码的执行效率。

2.方便管控线程并发数。

3.Java中一共有 4 种线程池:

1.可缓存线程池,不固定线程个数:

参数含义依次为:核心线程数,最大线程数,线程存活保持时间(空闲线程空闲时间大于存活时间就会被销毁),时间单位,任务队列。

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

    public static void main(String[] args) {
        //可以复用就复用,不能复用就新建线程。
        ExecutorService service = Executors.newCachedThreadPool();
        for(int i = 0;i < 100;i++){
            service.execute(new Runnable() {
                @Override
                public void run() {
                    System.out.println(Thread.currentThread().getName() + "正在执行");
                }
            });
        }

    }
}

一定程度上,减少了线程的创建和销毁,节约系统资源。

2.定长线程池,线程个数固定:

 public static ExecutorService newFixedThreadPool(int nThreads) {
        return new ThreadPoolExecutor(nThreads, nThreads,
                                      0L, TimeUnit.MILLISECONDS,
                                      new LinkedBlockingQueue());
    }
public class FixedThreadPool {
    public static void main(String[] args) {
        //固定线程个数的线程池
        ExecutorService service = Executors.newFixedThreadPool(3);
        for(int i = 0;i < 20;i++){
            service.execute(new Runnable() {
                @Override
                public void run() {
                    System.out.println(Thread.currentThread().getName() + "正在执行");
                }
            });
        }
    }
}

可控制最大线程并发数,超出的任务会在队列里面等待。

3.单线程化的线程池,核心线程数为1:

 public static ExecutorService newSingleThreadExecutor() {
        return new FinalizableDelegatedExecutorService
            (new ThreadPoolExecutor(1, 1,
                                    0L, TimeUnit.MILLISECONDS,
                                    new LinkedBlockingQueue()));
    }
public class SingleThreadExecutor {
    public static void main(String[] args) {
        //固定线程个数的线程池
        ExecutorService service = Executors.newSingleThreadExecutor();
        for(int i = 0;i < 20;i++){
            service.execute(new Runnable() {
                @Override
                public void run() {
                    System.out.println(Thread.currentThread().getName() + "正在执行");
                }
            });
        }
    }
}

1.只有一个线程在工作;2.所有任务按顺序执行,即遵循队列的入队出队规则。

4.周期性或者延后执行的线程池:

public class ScheduledThreadPool {
    public static void main(String[] args) {
        //延迟执行
        ScheduledExecutorService service = Executors.newScheduledThreadPool(3);
//        service.schedule(new Runnable() {
//            @Override
//            public void run() {
//                System.out.println(Thread.currentThread().getName() + "延迟一秒执行");
//            }
//        }, 1, TimeUnit.SECONDS);
        //周期性执行
        service.scheduleAtFixedRate(new Runnable() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName() + "延迟一秒后,每3秒执行一次");
            }
        },1,3,TimeUnit.SECONDS);

    }

注意:此线程池和前面三个都不同,前三个都是ThreadPoolExecutor配置不同的实例,最后一个是ScheduledThreadPoolExecutor的实例。

4.线程池原理

从数据结构角度来说,线程池主要采用了阻塞队列和HashSet集合;从流程角度来看,当提交任务时:

1.线程池当前线程数 < 核心线程数,则直接创建新核心线程执行任务,即使此时有空闲线程。

2.当前线程 >= 核心线程数,将任务放入阻塞队列中。

3.如果阻塞队列放满了,那么如果当前线程 < 最大线程数,则新建一个线程执行任务。

4.如果当前线程 >= 最大线程数,那么执行饱和策略。

5.信号量

用于进程间同步或者同一个进程的线程同步。

可以保证两个或者多个关键代码段不被并发调用。当一个线程进入关键代码段时,需要先获取信号量,运行完了,要释放信号量。其他线程要想进去,必须等第一个线程释放信号量。类似同步锁。

6.线程池有哪几种工作队列

1.ArrayBlockingQueue

该队列是基于数组结构实现的有界阻塞队列,遵循FIFO(先进先出)原则。

2.LinkedBlockingQueue

基于链表结构实现的阻塞数组,遵循FIFO(先进先出)原则。吞吐量大于ArrayBlockingQueue。定长和单线程化线程池使用这个队列。

3.SynchronousQueue

是一个不存储元素的阻塞队列。每次插入之前要等待另一个线程调用移除方法,不然插入操作会一直等待着。吞吐量通常大于LinkedBlockingQueue,可缓存线程池使用这个队列。

4.PriorityBlockingQueue

一个具有优先级的无限阻塞队列。

7.多线程中的安全队列一般由什么来实现?

Java提供的线程安全的Queue分为阻塞队列和非阻塞队列,阻塞队列如:BlockingQueue,非阻塞队列如:ConcurrentLinkedQueue。

对于BlockingQueue要实现阻塞功能,需要调用put(e)和take()方法,ConcurrentLinkedQueue是基于链接节点的,无界的,线程安全的非阻塞队列。

 

参考文章:https://juejin.im/post/5e5c5c52f265da575f4e7558#heading-104

 

你可能感兴趣的:(Java基础知识)