3 线程池

1 阻塞队列

1.1 阻塞队列概述

  • 阻塞队列,顾名思义,首先它是一个队列, 通过一个共享的队列,可以使得数据 由队列的一端输入,从另外一端输出

1 当队列是空的,从队列中获取元素的操作将会被阻塞
2 当队列是满的,从队列中添加元素的操作将会被阻塞
3 试图从空的队列中获取元素的线程将会被阻塞,直到其他线程往空的队列插入新的元素
4 试图向已满的队列中添加新元素的线程将会被阻塞,直到其他线程从队列中移除一个或多个元素或者完全清空,使队列变得空闲起来并后续新增

在这里插入图片描述

1.2 阻塞队列架构

在这里插入图片描述

1.3 阻塞队列种类

  • ArrayBlockingQueue: 由数组结构组成的有界阻塞队列
  • LinkedBlockingQueue: 由链表结构组成的有界(但大小默认值 Integer>MAX_VAL UE)阻塞队列.
  • SynchronousQueue:不存储元素的阻塞队列,也即是单个元素的队列.
  • PriorityBlockingQueue:支持优先级排序的无界阻塞队列
  • LinkedTransferQueue:由链表结构组成的无界阻塞队列.
  • LinkedBlockingDeque:由了解结构组成的双向阻塞队列.

1.3.1 ArrayBlockingQueue

定义

  • ArrayBlockingQueue是一个有界缓存等待队列,可以指定缓存队列的大小

代码

@Slf4j
public class ThreadDemo1 {
    public static void main(String[] args) {
        BlockingQueue<Integer> queue = new ArrayBlockingQueue<>(3);
        //生产者线程
        Thread t1 = new Thread(() -> {
            for (int i = 0; i < 5; i++) {
                try {
                    queue.put(i);
                    log.info("装入数据:" + i);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        //消费者线程
        Thread t2 = new Thread(() -> {
            while (true) {
                try {
                    log.info("2秒后取数据");
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                try {
                    log.info("获取数据:" + queue.take());
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        t1.start();
        t2.start();
    }
}
// 结果
22:35:42 [INFO ] [Thread-1] c.i.j.ThreadDemo1 - 2秒后取数据
22:35:42 [INFO ] [Thread-0] c.i.j.ThreadDemo1 - 装入数据:0
22:35:42 [INFO ] [Thread-0] c.i.j.ThreadDemo1 - 装入数据:1
22:35:42 [INFO ] [Thread-0] c.i.j.ThreadDemo1 - 装入数据:2
22:35:44 [INFO ] [Thread-1] c.i.j.ThreadDemo1 - 获取数据:0
22:35:44 [INFO ] [Thread-0] c.i.j.ThreadDemo1 - 装入数据:3
22:35:44 [INFO ] [Thread-1] c.i.j.ThreadDemo1 - 2秒后取数据
22:35:46 [INFO ] [Thread-1] c.i.j.ThreadDemo1 - 获取数据:1
22:35:46 [INFO ] [Thread-0] c.i.j.ThreadDemo1 - 装入数据:4
22:35:46 [INFO ] [Thread-1] c.i.j.ThreadDemo1 - 2秒后取数据
22:35:48 [INFO ] [Thread-1] c.i.j.ThreadDemo1 - 获取数据:2
22:35:48 [INFO ] [Thread-1] c.i.j.ThreadDemo1 - 2秒后取数据
22:35:50 [INFO ] [Thread-1] c.i.j.ThreadDemo1 - 获取数据:3
22:35:50 [INFO ] [Thread-1] c.i.j.ThreadDemo1 - 2秒后取数据
22:35:52 [INFO ] [Thread-1] c.i.j.ThreadDemo1 - 获取数据:4
22:35:52 [INFO ] [Thread-1] c.i.j.ThreadDemo1 - 2秒后取数据

1.3.2 LinkedBlockingQueue

定义

  • 由链表结构组成的有界(但大小默认值 Integer>MAX_VAL UE)阻塞队列。

代码

public class ThreadDemo2 {
    public static void main(String[] args) {
        BlockingQueue<Integer> queue = new LinkedBlockingQueue<>();
        //生产者线程
        Thread t1 = new Thread(() -> {
            for (int i = 0; i < 5; i++) {
                try {
                    queue.put(i);
                    log.info("装入数据:" + i);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        //消费者线程
        Thread t2 = new Thread(() -> {
            while (true) {
                try {
                    log.info("2秒后取数据");
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                try {
                    log.info("获取数据:" + queue.take());
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        t1.start();
        t2.start();
    }
}
// 结果
19:05:57 [INFO ] [Thread-1] c.i.j.ThreadDemo2 - 2秒后取数据
19:05:57 [INFO ] [Thread-0] c.i.j.ThreadDemo2 - 装入数据:0
19:05:57 [INFO ] [Thread-0] c.i.j.ThreadDemo2 - 装入数据:1
19:05:57 [INFO ] [Thread-0] c.i.j.ThreadDemo2 - 装入数据:2
19:05:57 [INFO ] [Thread-0] c.i.j.ThreadDemo2 - 装入数据:3
19:05:57 [INFO ] [Thread-0] c.i.j.ThreadDemo2 - 装入数据:4
19:05:59 [INFO ] [Thread-1] c.i.j.ThreadDemo2 - 获取数据:0
19:05:59 [INFO ] [Thread-1] c.i.j.ThreadDemo2 - 2秒后取数据
19:06:01 [INFO ] [Thread-1] c.i.j.ThreadDemo2 - 获取数据:1
19:06:01 [INFO ] [Thread-1] c.i.j.ThreadDemo2 - 2秒后取数据
19:06:03 [INFO ] [Thread-1] c.i.j.ThreadDemo2 - 获取数据:2
19:06:03 [INFO ] [Thread-1] c.i.j.ThreadDemo2 - 2秒后取数据
19:06:05 [INFO ] [Thread-1] c.i.j.ThreadDemo2 - 获取数据:3
19:06:05 [INFO ] [Thread-1] c.i.j.ThreadDemo2 - 2秒后取数据
19:06:07 [INFO ] [Thread-1] c.i.j.ThreadDemo2 - 获取数据:4
19:06:07 [INFO ] [Thread-1] c.i.j.ThreadDemo2 - 2秒后取数据

1.3.3 SynchronousQueue

定义

  • 不存储元素的阻塞队列,也即是单个元素的队列.

代码

public class ThreadDemo3 {
    public static void main(String[] args) {
        BlockingQueue<Integer> queue = new SynchronousQueue<>();
        //生产者线程
        Thread t1 = new Thread(() -> {
            for (int i = 0; i < 5; i++) {
                try {
                    queue.put(i);
                    log.info("装入数据:" + i);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        //消费者线程
        Thread t2 = new Thread(() -> {
            while (true) {
                try {
                    log.info("获取数据:" + queue.take());
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        t1.start();
        t2.start();
    }
}
// 结果
19:12:42 [INFO ] [Thread-0] c.i.j.ThreadDemo3 - 装入数据:0
19:12:42 [INFO ] [Thread-1] c.i.j.ThreadDemo3 - 获取数据:0
19:12:42 [INFO ] [Thread-0] c.i.j.ThreadDemo3 - 装入数据:1
19:12:42 [INFO ] [Thread-1] c.i.j.ThreadDemo3 - 获取数据:1
19:12:42 [INFO ] [Thread-1] c.i.j.ThreadDemo3 - 获取数据:2
19:12:42 [INFO ] [Thread-0] c.i.j.ThreadDemo3 - 装入数据:2
19:12:42 [INFO ] [Thread-0] c.i.j.ThreadDemo3 - 装入数据:3
19:12:42 [INFO ] [Thread-1] c.i.j.ThreadDemo3 - 获取数据:3
19:12:42 [INFO ] [Thread-0] c.i.j.ThreadDemo3 - 装入数据:4
19:12:42 [INFO ] [Thread-1] c.i.j.ThreadDemo3 - 获取数据:4

1.4 阻塞队列核心方法

请添加图片描述

抛出异常当阻塞队列满时,再往队列里add插入元素会抛IllegalStateException:Queue full
当阻塞队列空时,再往队列里remove移除元素会抛NoSuchElementException
特殊值插入方法,成功ture失败false
移除方法,成功返回出队列的元素,队列里没有就返回null
一直阻塞当阻塞队列满时,生产者线程继续往队列里put元素,队列会一直阻塞生产者线程直到put数据or响应中断退出
当阻塞队列空时,消费者线程试图从队列里take元素,队列会一直阻塞消费者线程直到队列可用
超时退出当阻塞队列满时,队列会阻塞生产者线程一定时间,超过限时后生产者线程会退出

1.4.1 测试抛异常

add

public class ThreadDemo4 {
    public static void main(String[] args) throws Exception {
        //创建阻塞队列
        BlockingQueue<String> blockingQueue = new ArrayBlockingQueue<>(3);
        log.info(blockingQueue.add("a") + "");
        log.info(blockingQueue.add("b") + "");
        log.info(blockingQueue.add("c") + "");
        log.info(blockingQueue.element() + "");
        log.info(blockingQueue.add("w") + "");
    }
}

// 结果
19:28:53 [INFO ] [main] c.i.j.ThreadDemo4 - true
19:28:53 [INFO ] [main] c.i.j.ThreadDemo4 - true
19:28:53 [INFO ] [main] c.i.j.ThreadDemo4 - true
19:28:53 [INFO ] [main] c.i.j.ThreadDemo4 - a
Exception in thread "main" java.lang.IllegalStateException: Queue full
	at java.util.AbstractQueue.add(AbstractQueue.java:98)
	at java.util.concurrent.ArrayBlockingQueue.add(ArrayBlockingQueue.java:312)
	at cn.itcast.juc.ThreadDemo4.main(ThreadDemo4.java:17)

remove

public class ThreadDemo4 {
    public static void main(String[] args) throws Exception {
        //创建阻塞队列
        BlockingQueue<String> blockingQueue = new ArrayBlockingQueue<>(3);
        log.info(blockingQueue.add("a") + "");
        log.info(blockingQueue.remove() + "");
        log.info(blockingQueue.remove() + "");
    }
}

// 结果
19:30:02 [INFO ] [main] c.i.j.ThreadDemo4 - true
19:30:02 [INFO ] [main] c.i.j.ThreadDemo4 - a
Exception in thread "main" java.util.NoSuchElementException
	at java.util.AbstractQueue.remove(AbstractQueue.java:117)
	at cn.itcast.juc.ThreadDemo4.main(ThreadDemo4.java:15)

1.4.2 测试特殊值

offer

public class ThreadDemo4 {
    public static void main(String[] args) throws Exception {
        BlockingQueue<String> blockingQueue = new ArrayBlockingQueue<>(3);
        System.out.println(blockingQueue.offer("a"));
        System.out.println(blockingQueue.offer("b"));
        System.out.println(blockingQueue.offer("c"));
        System.out.println(blockingQueue.peek());
        System.out.println(blockingQueue.offer("e"));
    }
}
// 结果
true
true
true
a
false

poll

public class ThreadDemo4 {
    public static void main(String[] args) throws Exception {
        BlockingQueue<String> blockingQueue = new ArrayBlockingQueue<>(3);
        System.out.println(blockingQueue.offer("a"));
        System.out.println(blockingQueue.poll());
        System.out.println(blockingQueue.poll());
    }
}
// 结果
true
a
null

1.4.3 测试阻塞

put

public class ThreadDemo4 {
    public static void main(String[] args) throws Exception {
        //创建阻塞队列
        BlockingQueue<String> blockingQueue = new ArrayBlockingQueue<>(3);
        blockingQueue.put("a");
        blockingQueue.put("b");
        blockingQueue.put("c");
        blockingQueue.put("w");
    }
}
// 结果
线程卡主了

take

public class ThreadDemo4 {
    public static void main(String[] args) throws Exception {
        //创建阻塞队列
        BlockingQueue<String> blockingQueue = new ArrayBlockingQueue<>(3);
        blockingQueue.put("a");
        System.out.println(blockingQueue.take());
        System.out.println(blockingQueue.take());
    }
}
// 结果
a
线程卡主了

2 线程池介绍

定义

  • 线程池是为突然大量爆发的线程设计的,通过有限的几个固定线程为大量的操作服务,通过对线程的复用减少了创建和销毁线程所需的时间,从而提高效率。

优势

  • 1 降低资源消耗。通过重复利用已创建的线程降低线程创建和销毁造成的消耗。
  • 2 提高响应速度。当任务到达时,任务可以不需要等到线程创建就能立即执行。
  • 3 提高线程的可管理性。线程是稀缺资源,如果无限制地创建,不仅会消耗系统资源,还会降低系统的稳定性,使用线程池可以进行统一分配、调优和监控。

3 线程池体系结构

在这里插入图片描述

java.util.concurrent.Executor 负责线程的使用和调度的根接口
		|--ExecutorService 子接口: 线程池的主要接口
				|--ThreadPoolExecutor 线程池的实现类
				|--ScheduledExceutorService 子接口: 负责线程的调度
					|--ScheduledThreadPoolExecutor : 继承ThreadPoolExecutor,实现了ScheduledExecutorService
工具类 : Executors 提供快捷的创建线程池的方法

4 线程池七大参数

public ThreadPoolExecutor(int corePoolSize,
                          int maximumPoolSize,
                          long keepAliveTime,
                          TimeUnit unit,
                          BlockingQueue<Runnable> workQueue,
                          ThreadFactory threadFactory,
                          RejectedExecutionHandler handler) {
    if (corePoolSize < 0 ||
        maximumPoolSize <= 0 ||
        maximumPoolSize < corePoolSize ||
        keepAliveTime < 0)
        throw new IllegalArgumentException();
    if (workQueue == null || threadFactory == null || handler == null)
        throw new NullPointerException();
    this.corePoolSize = corePoolSize;
    this.maximumPoolSize = maximumPoolSize;
    this.workQueue = workQueue;
    this.keepAliveTime = unit.toNanos(keepAliveTime);
    this.threadFactory = threadFactory;
    this.handler = handler;
}
序号名称类型含义
1corePoolSizeint核心线程池大小
2maximumPoolSizeint最大线程池大小
3keepAliveTimelong线程最大空闲时间
4unitTimeUnit时间单位
5workQueueBlockingQueue线程等待队列
6threadFactoryThreadFactory线程创建工厂
7handlerRejectedExecutionHandler拒绝策略

5 线程池执行流程

代码

public class ThreadDemo5 {
    public static void main(String[] args) {
        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
                2,
                5,
                10,
                TimeUnit.SECONDS,
                new ArrayBlockingQueue<Runnable>(10),
                new ThreadPoolExecutor.AbortPolicy());

        for (int i = 0; i < 20; i++) {
            try {
                threadPoolExecutor.execute(new MyRunnable("第" + (i + 1) + "号线程"));
            } catch (Exception e) {
                log.info("丢弃任务:" + (i + 1));
            }

        }
    }

    static class MyRunnable implements Runnable {
        private String name;
        public MyRunnable(String name) {
            this.name = name;
        }

        @Override
        public void run() {
            log.info(name);
            while (true) {
                // 线程一直运行
            }
        }
    }
}

// 结果
20:20:10 [INFO ] [pool-1-thread-2] c.i.j.ThreadDemo5 -2号线程
20:20:10 [INFO ] [pool-1-thread-5] c.i.j.ThreadDemo5 -15号线程
20:20:10 [INFO ] [pool-1-thread-3] c.i.j.ThreadDemo5 -13号线程
20:20:10 [INFO ] [pool-1-thread-1] c.i.j.ThreadDemo5 -1号线程
20:20:10 [INFO ] [pool-1-thread-4] c.i.j.ThreadDemo5 -14号线程
20:20:10 [INFO ] [main] c.i.j.ThreadDemo5 - 丢弃任务:16
20:20:10 [INFO ] [main] c.i.j.ThreadDemo5 - 丢弃任务:17
20:20:10 [INFO ] [main] c.i.j.ThreadDemo5 - 丢弃任务:18
20:20:10 [INFO ] [main] c.i.j.ThreadDemo5 - 丢弃任务:19
20:20:10 [INFO ] [main] c.i.j.ThreadDemo5 - 丢弃任务:20

图示
在这里插入图片描述
说明

1、在创建了线程池后,开始等待请求。
2、当调用execute()方法添加一个请求任务时,线程池会做出如下判断:
  2.1如果正在运行的线程数量小于corePoolSize,那么马上创建线程运行这个任务;
  2.2如果正在运行的线程数量大于或等于corePoolSize,那么将这个任务放入队列;
  2.3如果这个时候队列满了且正在运行的线程数量还小于maximumPoolSize,那么还是要创建非核心线程立刻运行这个任务;
  2.4如果队列满了且正在运行的线程数量大于或等于maximumPoolSize,那么线程池会启动饱和拒绝策略来执行。
3、当一个线程完成任务时,它会从队列中取下一个任务来执行。
4、当一个线程无事可做超过一定的时间(keepAliveTime)时,线程会判断:
    如果当前运行的线程数大于corePoolSize,那么这个线程就被停掉。
    所以线程池的所有任务完成后,它最终会收缩到corePoolSize的大小。

流程图
在这里插入图片描述

6 线程池饱和策略

6.1 定义

  • 等待队列已经排满了,再也塞不下新任务了,同时,线程池中的max线程也达到了,无法继续为新任务服务。这个是时候我们就需要拒绝策略机制合理的处理这个问题

6.2 AbortPolicy(默认)

说明

  • 直接抛出RejectedExecutionException异常阻止系统正常运行

代码

public class ThreadDemo6 {
    public static void main(String[] args) {
        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
                2,
                3,
                10,
                TimeUnit.SECONDS,
                new ArrayBlockingQueue<Runnable>(3),
                new ThreadPoolExecutor.AbortPolicy());

        for (int i = 0; i < 10; i++) {
            try {
                threadPoolExecutor.execute(new MyRunnable("第" + (i + 1) + "号线程"));
            } catch (Exception e) {
                //e.printStackTrace();
                log.info("丢弃任务:" + (i + 1));
            }
        }
    }

    static class MyRunnable implements Runnable {
        private String name;
        public MyRunnable(String name) {
            this.name = name;
        }

        @Override
        public void run() {
            System.out.println(name);
            while (true) {
                // 线程一直运行
            }
        }
    }
}
// 结果2号线程
第1号线程
第6号线程
22:28:58 [INFO ] [main] c.i.j.ThreadDemo6 - 丢弃任务:7
22:28:58 [INFO ] [main] c.i.j.ThreadDemo6 - 丢弃任务:8
22:28:58 [INFO ] [main] c.i.j.ThreadDemo6 - 丢弃任务:9
22:28:58 [INFO ] [main] c.i.j.ThreadDemo6 - 丢弃任务:10

6.3 CallerRunsPolicy

说明

  • “调用者运行”一种调节机制,该策略既不会抛弃任务,也不会抛出异常,而是将某些任务回退到调用者,从而降低新任务的流量。

代码

public class ThreadDemo7 {
    public static void main(String[] args) {
        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
                2,
                3,
                10,
                TimeUnit.SECONDS,
                new ArrayBlockingQueue<Runnable>(3),
                new ThreadPoolExecutor.CallerRunsPolicy());

        for (int i = 0; i < 10; i++) {
            try {
                threadPoolExecutor.execute(new ThreadDemo7.MyRunnable("第" + (i + 1) + "号线程"));
            } catch (Exception e) {
                //e.printStackTrace();
                log.info("丢弃任务:" + (i + 1));
            }
        }
    }
    static class MyRunnable implements Runnable {
        private String name;
        public MyRunnable(String name) {
            this.name = name;
        }

        @Override
        public void run() {
            log.info(name);
            while (true) {
                // 线程一直运行
            }
        }
    }
}
// 结果
22:40:33 [INFO ] [pool-1-thread-3] c.i.j.ThreadDemo7 -6号线程
22:40:33 [INFO ] [main] c.i.j.ThreadDemo7 -7号线程
22:40:33 [INFO ] [pool-1-thread-2] c.i.j.ThreadDemo7 -2号线程
22:40:33 [INFO ] [pool-1-thread-1] c.i.j.ThreadDemo7 -1号线程

6.4 DiscardOldestPolicy

说明

  • 抛弃队列中等待最久的任务,然后把当前任务加人队列中尝试再次提交当前任务。

代码

public class ThreadDemo7 {
    public static void main(String[] args) {
        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
                2,
                3,
                10,
                TimeUnit.SECONDS,
                new ArrayBlockingQueue<Runnable>(3),
                new ThreadPoolExecutor.DiscardOldestPolicy());

        for (int i = 0; i < 7; i++) {
            try {
                threadPoolExecutor.execute(new ThreadDemo7.MyRunnable("第" + (i + 1) + "号线程"));
            } catch (Exception e) {
                //e.printStackTrace();
                log.info("丢弃任务:" + (i + 1));
            }
        }
    }
    static class MyRunnable implements Runnable {
        private String name;
        public MyRunnable(String name) {
            this.name = name;
        }

        @Override
        public void run() {
            log.info(name);
            try {
                TimeUnit.SECONDS.sleep(5);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

// 结果 丢弃了3号线程
20:38:39 [INFO ] [pool-1-thread-3] c.i.j.ThreadDemo7 -6号线程
20:38:39 [INFO ] [pool-1-thread-2] c.i.j.ThreadDemo7 -2号线程
20:38:39 [INFO ] [pool-1-thread-1] c.i.j.ThreadDemo7 -1号线程
20:38:44 [INFO ] [pool-1-thread-3] c.i.j.ThreadDemo7 -7号线程
20:38:44 [INFO ] [pool-1-thread-2] c.i.j.ThreadDemo7 -4号线程
20:38:44 [INFO ] [pool-1-thread-1] c.i.j.ThreadDemo7 -5号线程

6.5 DiscardPolicy

定义

  • 该策略默默地丢弃无法处理的任务,不予任何处理也不抛出异常。如果允许任务丢失,这是最好的一种策略

代码

public class ThreadDemo7 {
    public static void main(String[] args) {
        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
                2,
                3,
                10,
                TimeUnit.SECONDS,
                new ArrayBlockingQueue<Runnable>(3),
                new ThreadPoolExecutor.DiscardPolicy());

        for (int i = 0; i < 10; i++) {
            try {
                threadPoolExecutor.execute(new ThreadDemo7.MyRunnable("第" + (i + 1) + "号线程"));
            } catch (Exception e) {
                //e.printStackTrace();
                log.info("丢弃任务:" + (i + 1));
            }
        }
    }
    static class MyRunnable implements Runnable {
        private String name;
        public MyRunnable(String name) {
            this.name = name;
        }

        @Override
        public void run() {
            log.info(name);
            try {
                TimeUnit.SECONDS.sleep(2);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}
// 结果 后面的都被抛弃了
20:42:20 [INFO ] [pool-1-thread-3] c.i.j.ThreadDemo7 -6号线程
20:42:20 [INFO ] [pool-1-thread-1] c.i.j.ThreadDemo7 -1号线程
20:42:20 [INFO ] [pool-1-thread-2] c.i.j.ThreadDemo7 -2号线程
20:42:23 [INFO ] [pool-1-thread-2] c.i.j.ThreadDemo7 -4号线程
20:42:23 [INFO ] [pool-1-thread-1] c.i.j.ThreadDemo7 -3号线程
20:42:23 [INFO ] [pool-1-thread-3] c.i.j.ThreadDemo7 -5号线程

7 线程池工具类

Executors是线程池的工具类,提供了四种快捷创建线程池的方法:

  • newCachedThreadPool

    创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。

  • newFixedThreadPool

    创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。

  • newSingleThreadExecutor

    创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。

  • newScheduledThreadPool

    创建一个定时线程池,支持定时及周期性任务执行。

7.1 newFixedThreadPool

源码

// Executors.newFixedThreadPool(int) : 一池定线程
public static ExecutorService newFixedThreadPool(int nThreads) {
    return new ThreadPoolExecutor(nThreads, nThreads,
                                  0L, TimeUnit.MILLISECONDS,
                                  new LinkedBlockingQueue<Runnable>());
}

说明

1 corePoolSize和maximumPoolSize相等
2 使用的是LinkedBlockingQueue

代码

public class ThreadDemo8 {
    public static void main(String[] args) throws Exception {
        // 创建线程池
        ExecutorService es = Executors.newFixedThreadPool(2);
        // 会创建出3个线程   分别执行任务
        for (int i = 0; i < 3; i++) {
            es.execute(()->{
                for (int j = 0; j < 3; j++) {
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    log.info(Thread.currentThread().getName() + ":" + j);
                }
            });
        }
        es.shutdown();
    }
}
// 结果
22:21:32 [INFO ] [pool-1-thread-2] c.i.j.ThreadDemo8 - pool-1-thread-2:0
22:21:32 [INFO ] [pool-1-thread-1] c.i.j.ThreadDemo8 - pool-1-thread-1:0
22:21:33 [INFO ] [pool-1-thread-2] c.i.j.ThreadDemo8 - pool-1-thread-2:1
22:21:33 [INFO ] [pool-1-thread-1] c.i.j.ThreadDemo8 - pool-1-thread-1:1
22:21:34 [INFO ] [pool-1-thread-1] c.i.j.ThreadDemo8 - pool-1-thread-1:2
22:21:34 [INFO ] [pool-1-thread-2] c.i.j.ThreadDemo8 - pool-1-thread-2:2
22:21:35 [INFO ] [pool-1-thread-2] c.i.j.ThreadDemo8 - pool-1-thread-2:0
22:21:36 [INFO ] [pool-1-thread-2] c.i.j.ThreadDemo8 - pool-1-thread-2:1
22:21:37 [INFO ] [pool-1-thread-2] c.i.j.ThreadDemo8 - pool-1-thread-2:2

7.2 newSingleThreadExecutor

源码

// Executors.newSingleThreadExecutor( ) : 一池一线程
public static ExecutorService newSingleThreadExecutor() {
    return new FinalizableDelegatedExecutorService
        (new ThreadPoolExecutor(1, 1,
                                0L, TimeUnit.MILLISECONDS,
                                new LinkedBlockingQueue<Runnable>()));
}

说明

1 corePoolSize和maximumPoolSize都为1
2 使用的是LinkedBlockingQueue

代码

public class ThreadDemo8 {
    public static void main(String[] args) throws Exception {
        // 创建线程池
        ExecutorService es = Executors.newSingleThreadExecutor();
        // 会创建出10个线程   分别执行任务
        for (int i = 0; i < 3; i++) {
            es.execute(()->{
                for (int j = 0; j < 3; j++) {
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    log.info(Thread.currentThread().getName() + ":" + j);
                }
            });
        }
        es.shutdown();
    }
}
// 结果
22:27:28 [INFO ] [pool-1-thread-1] c.i.j.ThreadDemo8 - pool-1-thread-1:0
22:27:29 [INFO ] [pool-1-thread-1] c.i.j.ThreadDemo8 - pool-1-thread-1:1
22:27:30 [INFO ] [pool-1-thread-1] c.i.j.ThreadDemo8 - pool-1-thread-1:2
22:27:31 [INFO ] [pool-1-thread-1] c.i.j.ThreadDemo8 - pool-1-thread-1:0
22:27:32 [INFO ] [pool-1-thread-1] c.i.j.ThreadDemo8 - pool-1-thread-1:1
22:27:33 [INFO ] [pool-1-thread-1] c.i.j.ThreadDemo8 - pool-1-thread-1:2
22:27:34 [INFO ] [pool-1-thread-1] c.i.j.ThreadDemo8 - pool-1-thread-1:0
22:27:35 [INFO ] [pool-1-thread-1] c.i.j.ThreadDemo8 - pool-1-thread-1:1
22:27:36 [INFO ] [pool-1-thread-1] c.i.j.ThreadDemo8 - pool-1-thread-1:2

7.3 newCachedThreadPool

源码

// Executors.newCachedThreadPool( ) : 一池N线程
public static ExecutorService newCachedThreadPool() {
    return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                  60L, TimeUnit.SECONDS,
                                  new SynchronousQueue<Runnable>());
}

说明

1 corePoolSize是0,maximumPoolSize是Integer.MAX_VALUE
2 使用的是SynchronousQueue

代码

public class ThreadDemo8 {
    public static void main(String[] args) throws Exception {
        // 创建线程池
        ExecutorService es = Executors.newCachedThreadPool();
        // 会创建出10个线程   分别执行任务
        for (int i = 0; i < 3; i++) {
            es.execute(()->{
                for (int j = 0; j < 3; j++) {
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    log.info(Thread.currentThread().getName() + ":" + j);
                }
            });
        }
        es.shutdown();
    }
}
// 结果
22:31:07 [INFO ] [pool-1-thread-2] c.i.j.ThreadDemo8 - pool-1-thread-2:0
22:31:07 [INFO ] [pool-1-thread-3] c.i.j.ThreadDemo8 - pool-1-thread-3:0
22:31:07 [INFO ] [pool-1-thread-1] c.i.j.ThreadDemo8 - pool-1-thread-1:0
22:31:08 [INFO ] [pool-1-thread-3] c.i.j.ThreadDemo8 - pool-1-thread-3:1
22:31:08 [INFO ] [pool-1-thread-2] c.i.j.ThreadDemo8 - pool-1-thread-2:1
22:31:08 [INFO ] [pool-1-thread-1] c.i.j.ThreadDemo8 - pool-1-thread-1:1
22:31:08 [INFO ] [pool-1-thread-3] c.i.j.ThreadDemo8 - pool-1-thread-3:2
22:31:08 [INFO ] [pool-1-thread-2] c.i.j.ThreadDemo8 - pool-1-thread-2:2
22:31:08 [INFO ] [pool-1-thread-1] c.i.j.ThreadDemo8 - pool-1-thread-1:2

7.4 newScheduledThreadPool

源码

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

public ScheduledThreadPoolExecutor(int corePoolSize) {
    super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
          new DelayedWorkQueue());
}

说明

1 corePoolSize是用户传入,maximumPoolSize是Integer.MAX_VALUE
2 使用的是DelayedWorkQueue

代码

// 延迟n秒后开启任务
public class ThreadDemo8 {
    public static void main(String[] args) throws Exception {
        ScheduledExecutorService newScheduledThreadPool = Executors.newScheduledThreadPool(5);
        // 延迟执行任务(任务执行一次)
        // 参数1:任务   参数2:延迟时间   参数3:时间单位
        log.info("任务开始");
        newScheduledThreadPool.schedule(()->log.info("要执行的任务"), 3, TimeUnit.SECONDS);
    }
}
// 结果
22:38:17 [INFO ] [main] c.i.j.ThreadDemo8 - 任务开始
22:38:20 [INFO ] [pool-1-thread-1] c.i.j.ThreadDemo8 - 要执行的任务
// 周期任务
public class ThreadDemo8 {
    public static void main(String[] args) throws Exception {
        ScheduledExecutorService newScheduledThreadPool = Executors.newScheduledThreadPool(5);
        // 周期性执行任务(任务会执行多次)
        log.info("任务开始");
        // 参数1:任务   参数2:延迟时间   参数3:每隔长时间   参数4:时间单位
        newScheduledThreadPool.scheduleAtFixedRate(()-> log.info("要执行的任务"), 3, 2, TimeUnit.SECONDS);
    }
}

// 结果
22:40:03 [INFO ] [main] c.i.j.ThreadDemo8 - 任务开始
22:40:06 [INFO ] [pool-1-thread-1] c.i.j.ThreadDemo8 - 要执行的任务
22:40:08 [INFO ] [pool-1-thread-1] c.i.j.ThreadDemo8 - 要执行的任务
22:40:10 [INFO ] [pool-1-thread-2] c.i.j.ThreadDemo8 - 要执行的任务
22:40:12 [INFO ] [pool-1-thread-1] c.i.j.ThreadDemo8 - 要执行的任务

8 线程池源码分析

8.1 worker内部源码

在线程池的实现中,Worker这个类是线程池的内部类,Worker对象是线程池实现的核心。ThreadPoolExecutor中存放了一个

private final HashSet<Worker> workers = new HashSet<Worker>();

点进Worker类的源码中 发现Worker实现了Runnable接口,并且有两个属性一个线程对象,还有一个第一次要执行的任务

private final class Worker
        extends AbstractQueuedSynchronizer
        implements Runnable

{
		// 执行工作的线程对象
        final Thread thread;
		// Worker要执行的第一个任务
        Runnable firstTask;
}

查看Worker的构造方法

Worker(Runnable firstTask) {
            setState(-1); 
    		// 传入worker第一次要执行的任务
            this.firstTask = firstTask;
    		// 使用工厂对象创建线程, 并把worker本身传入
            this.thread = getThreadFactory().newThread(this);
}

查看线程工厂对象的newThread类方法

public Thread newThread(Runnable r) {
    // new 了一个新的线程对象  并且把worker对象作为线程任务传入
            Thread t = new Thread(group, r,
                                  namePrefix + threadNumber.getAndIncrement(),
                                  0);
            return t;
}

查看Worker类当中的run方法

// 线程任务 run方法
public void run() {
   runWorker(this);
}
// Worker的核心工作方法
final void runWorker(Worker w) {
        Thread wt = Thread.currentThread();
        // 第一次要执行的任务 赋值给task
        Runnable task = w.firstTask;
        ...
        try {
            // getTask()是指从任务队列中获取任务
            while (task != null || (task = getTask()) != null) {
                w.lock();
                ...
                try {
                    ...
                    try {
                        // 执行任务的run方法
                        task.run();
                    } 
                    ...
                } finally {
                    task = null;
                    w.completedTasks++;
                    w.unlock();
                }
            }
            completedAbruptly = false;
        } finally {
            processWorkerExit(w, completedAbruptly);
        }
    }

查看getTask方法如何获取任务

private Runnable getTask() {
    boolean timedOut = false; 
    for (;;) {
        ...
            try {
                Runnable r = timed ?
                    // 下面两种方法都是在从 workQueue队列中获取任务
                    workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
                    workQueue.take();
                // 将取到的任务返回
                if (r != null)
                    return r;
                timedOut = true;
            } catch (InterruptedException retry) {
                timedOut = false;
            }
    }
}

小结

小结:
通过上面的源码分析得出,Worker对象是线程池工作的核心,一个Worker对象代表一个工作线程, 只要Worker内的线程thread的start方法被调用后,我们的worker对象内的run方法被线程执行,而run方法中则不断的从任务队列中获取 任务,并调用任务的run方法来执行,这样就达到了线程复用的目的。

8.2 execute的执行源码

// execute执行方法源码分析
public void execute(Runnable command) {
    // 任务为空抛异常
    if (command == null)
        throw new NullPointerException();
    // ctl 是 integer原子类  主要通过它记录两类信息,
    // ctl作用: 1.记录线程池状态    2.记录线程池工作线程数量 
    // workerCountOf(c):判断worker数量 
    // isRunning(c): 判断线程池状态
    // 1.如果当前worker数量小于corePoolSize  创建一个新的worker
    int c = ctl.get();
    if (workerCountOf(c) < corePoolSize) {
        // 创建worker 参数1:任务 参数2:添加核心还是临时线程
        if (addWorker(command, true))
            return;
        c = ctl.get();
    }
    // 2.尝试向任务队列中添加任务,如果添加失败进入下移流程
    if (isRunning(c) && workQueue.offer(command)) {
        // 添加成功  复查线程池状态
        int recheck = ctl.get();
        if (! isRunning(recheck) && remove(command))
            reject(command);
        else if (workerCountOf(recheck) == 0)
            addWorker(null, false);
    }
    // 3.参数2:true => 添加核心工作线程   false => 添加临时工作线程   
    else if (!addWorker(command, false))

        // 4. 如果添加失败,执行拒绝策略
        reject(command);
}

通过execute方法的源码,我们就已经看到了执行流程,在查看addWorker的方法,看看是如何添加一个worker的

private boolean addWorker(Runnable firstTask, boolean core) {
    retry:
    for (;;) {
        ...
        for (;;) {
            // wc是工作线程的数量
            // core为true 判断是否大于核心线程数量
            // core为false 判断是否大于最大线程数量
            int wc = workerCountOf(c);
            if (wc >= CAPACITY ||
                wc >= (core ? corePoolSize : maximumPoolSize))
                return false;
            ...
        }
    }

    boolean workerStarted = false;
    boolean workerAdded = false;
    // 准备创建worker
    Worker w = null;
    try {
        // 创建worker对象,构造器内会通过线程工厂创建一个线程  并且把worker对象作为任务传入
        w = new Worker(firstTask);
        final Thread t = w.thread;
        if (t != null) {
            final ReentrantLock mainLock = this.mainLock;
            mainLock.lock();
            try {
                    ...
                    // 将worker对象 存入到workers集合
                    workers.add(w);
                    int s = workers.size();
                    if (s > largestPoolSize)
                        largestPoolSize = s;
                    workerAdded = true;
                }
            } finally {
                mainLock.unlock();
            }
            if (workerAdded) {
                // 启动worker内的线程
                t.start();
                workerStarted = true;
            }
        }
    } finally {
        if (! workerStarted)
            addWorkerFailed(w);
    }
    return workerStarted;
}

小结:

可以看到,在execute方法中完全和我们前面将的流程一样,在addWorker方法中通过构造器创建了worker对象 并把它存入到了workers集合中,然后启动worker内线程的start方法,这样这个worker就会不断工作,不断的执行任务队列里面的任务

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值