6、共享模型之线程池

该栏目讲叙多线程基础、共享模型的内存、管程、无锁、不可变设计和多线程并发工具


自定义线程池

1、背景

  • :线程是一种系统资源,每创建一个线程都要占用一定的内存。在高并发的情况下,如果为每个任务都创建一个新的线程,对内存的占用是相当大的;从 CPU 的角度出发,CPU 的数量有限,如果线程数越多,那么没有获取到 CPU 时间片的线程会被阻塞,引发线程上下文切换,从而导致系统性能。所以创建一批线程,让线程得到充分的利用,这样减少内存的占用,也可以减少线程的数量,避免它们频繁的发生上下文切换

2、图解

线程池图解

3、实现

  • 拒绝策略接口
@FunctionalInterface
public interface RejectPolicy<T> {

    void reject(BlockingQueue<T> queue, T task);

}
  • 阻塞队列
/**
 * 阻塞队列
 */
public class BlockingQueue<T> {

    /**
     * 任务容量
     */
    private final int capacity;

    /**
     * 任务队列
     */
    private final Deque<T> queue = new ArrayDeque<>();

    /**
     * 锁
     */
    private final ReentrantLock lock = new ReentrantLock();

    /**
     * 消费者条件
     */
    Condition emptyWaitSet = lock.newCondition();

    /**
     * 生产者条件
     */
    Condition fullWaitSet = lock.newCondition();

    public BlockingQueue(int capacity) {
        this.capacity = capacity;
    }

    /**
     * 获取任务
     *
     * @return 任务
     */
    public T take() {
        lock.lock();
        try {
            while (queue.isEmpty()) {
                try {
                    System.out.println("任务队列串没有任务了...");
                    emptyWaitSet.await();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            final T task = queue.removeFirst();
            System.out.println("获取的任务:" + task);
            emptyWaitSet.signal();
            return task;
        } finally {
            lock.unlock();
        }
    }

    /**
     * 获取任务,带超时
     *
     * @param timeout  超时时间
     * @param timeUnit 超时单位
     * @return 任务
     */
    public T take(long timeout, TimeUnit timeUnit) {
        lock.lock();
        try {
            long nanos = timeUnit.toNanos(timeout);
            while (queue.isEmpty()) {
                try {
                    if (nanos <= 0) {
                        System.out.println("获取任务超时...");
                        return null;
                    }
                    System.out.println("任务队列串没有任务了...");
                    nanos = emptyWaitSet.awaitNanos(nanos);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            final T task = queue.removeFirst();
            System.out.println("获取的任务:" + task);
            emptyWaitSet.signal();
            return task;
        } finally {
            lock.unlock();
        }
    }

    /**
     * 添加任务
     *
     * @param task 任务
     */
    public void put(T task) {
        lock.lock();
        try {
            while (queue.size() == capacity) {
                try {
                    System.out.println("任务队列已经存储满...");
                    fullWaitSet.await();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            System.out.println("任务加入队列:" + task);
            queue.addLast(task);
            emptyWaitSet.signal();
        } finally {
            lock.unlock();
        }
    }

    /**
     * 添加任务,带超时
     *
     * @param task     任务
     * @param timeout  超时时间
     * @param timeUnit 超时单位
     */
    public void put(T task, long timeout, TimeUnit timeUnit) {
        lock.lock();
        try {
            long nanos = timeUnit.toNanos(timeout);
            while (queue.size() == capacity) {
                try {
                    System.out.println("任务队列已经存储满...");
                    nanos = fullWaitSet.awaitNanos(nanos);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            System.out.println("任务加入队列:" + task);
            queue.addLast(task);
            emptyWaitSet.signal();
        } finally {
            lock.unlock();
        }
    }

    /**
     * 策略添加任务
     *
     * @param task   任务
     * @param policy 策略
     */
    public void put(T task, RejectPolicy<T> policy) {
        lock.lock();
        try {
            if (queue.size() == capacity) {
                policy.reject(this, task);
            } else {
                System.out.println("任务加入队列:" + task);
                queue.addLast(task);
                emptyWaitSet.signal();
            }
        } finally {
            lock.unlock();
        }
    }

    /**
     * 获取队列大小
     *
     * @return 队列大小
     */
    public int getSize() {
        lock.lock();
        try {
            return queue.size();
        } finally {
            lock.unlock();
        }
    }

}
  • 线程池
/**
 * 线程池
 */
public class ThreadPool {

    /**
     * 线程核心数
     */
    private final int coreSize;

    /**
     * 任务队列
     */
    private final BlockingQueue<Runnable> taskQueue;

    /**
     * 线程集合
     */
    private final Set<Worker> workers = new HashSet<>();

    /**
     * 超时时长
     */
    private final long timeout;

    /**
     * 时间单位
     */
    private final TimeUnit timeUnit;

    /**
     * 拒绝策略
     */
    private final RejectPolicy<Runnable> policy;

    public ThreadPool(int coreSize, long timeout, TimeUnit timeUnit, int queueCapacity, 
    			RejectPolicy<Runnable> policy) {
        this.coreSize = coreSize;
        this.timeout = timeout;
        this.timeUnit = timeUnit;
        this.taskQueue = new BlockingQueue<>(queueCapacity);
        this.policy = policy;
    }

    /**
     * 执行任务
     *
     * @param task 任务
     */
    public void execute(Runnable task) {
        synchronized (workers) {
            if (workers.size() < coreSize) {
                Worker worker = new Worker(task);
                System.out.println("添加work:" + worker + ",task=" + task);
                workers.add(worker);
                worker.start();
            } else {
				// taskQueue.put(task);
                taskQueue.put(task, policy);
            }
        }
    }

    /**
     * 工作线程
     */
    class Worker extends Thread {

        private Runnable task;

        public Worker(Runnable task) {
            this.task = task;
        }

        @Override
        public void run() {
            while (task != null || (task = taskQueue.take(timeout, timeUnit)) != null) {
                try {
                    System.out.println("正在执行...task=" + task);
                    task.run();
                } catch (Exception e) {
                    e.printStackTrace();
                } finally {
                    task = null;
                }
            }
            synchronized (workers) {
                System.out.println("worker被移除,worker=" + this);
                workers.remove(this);
            }
        }
    }

}
  • 测试类
/**
 * 测试类
 */
public class TestDemo {

    public static void main(String[] args) {
        ThreadPool pool = new ThreadPool(2, 1000, TimeUnit.MILLISECONDS, 5, (queue, task) -> {
            // 一直等待
            queue.put(task);
            // 超时等待
            queue.put(task, 500, TimeUnit.MILLISECONDS);
            // 让调用者放弃任务
            System.out.println("放弃task=" + task);
            // 自己执行任务
            task.run();
            // 抛出异常
            throw new RuntimeException("任务执行失败,task=" + task);
        });
        for (int i = 0; i < 10; i++) {
            int index = i;
            pool.execute(() -> {
                try {
                    TimeUnit.SECONDS.sleep(10);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("执行任务,序号:" + index);
            });
        }
    }

}

ThreadPoolExecutor

1、线程池状态

线程池状态
注意: ThreadPoolExecutor 使用 int 类型的高 3 位表示线程池状态,低 29 位表示线程数量。目的是仅使用一次 CAS 原子操作进行赋值

2、构造方法

  • public ThreadPoolExecutor(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory, handler)
    • corePoolSize:核心线程数(最多保留的线程数)
    • maximumPoolSize:最大线程数,救援线程数 = 最大线程数 - 核心线程数
    • keepAliveTime:生存时间(针对救急线程)
    • unit:时间单位(针对救急线程)
    • workQueue:阻塞队列
    • threadFactory:线程工厂(为线程创建时起名称)
    • handler:拒绝策略
      • AbortPolicy:让调用者抛出 RejectedExecutionException 异常,这是默认策略
      • CallerRunsPolicy:让调用者运行任务
      • DiscardPolicy:放弃本次任务
      • DiscardOldestPolicy:放弃队列中早的任务,本任务取而代之

3、成员方法

描述方法
执行任务void execute(runnable)
执行任务,并可获取返回值Future submit(callback)
执行集合中所有任务List invokeAll(callbacks)
执行集合中任一任务T invokeAny(callbacks)
线程是否关闭boolean isShutdown()
线程是否终结boolean isTerminated()
指定时间内终结boolean awaitTermination(timeout, unit)

4、案例

public class ThreadPoolExecutorDemo {

    public static void main(String[] args) throws ExecutionException, InterruptedException {
        // 使用构造函数创建线程池
        final ThreadPoolExecutor pool = new ThreadPoolExecutor(2, 3, 1000,
                TimeUnit.MILLISECONDS, new SynchronousQueue<>());
        // 执行任务
        pool.execute(() -> System.out.println("执行任务1"));
        
        // 执行任务有返回值
        final Future<Integer> future = pool.submit(() -> {
            System.out.println("执行任务2");
            return 100;
        });
        System.out.println("执行结果:" + future.get());

        // 执行集合内所有任务
        List<Callable<Boolean>> tasks = new ArrayList<>();
        tasks.add(() -> {
            System.out.println("执行任务3");
            return true;
        });
        List<Future<Boolean>> results1 = pool.invokeAll(tasks);
        System.out.println("执行结果:" + results1);

        // 执行集合中任一任务
        Boolean result = pool.invokeAny(tasks);
        System.out.println("执行结果:" + result);

        // 其他方法
        System.out.println(pool.isShutdown());
        System.out.println(pool.isTerminated());
        pool.awaitTermination(1, TimeUnit.SECONDS);

        // 关闭线程池
        pool.shutdown();
    }

}

Executors 工具类

1、常用方法

  • Executors.newFixedThreadPool(coreSize):创建固定大小的线程池
  • Executors.newSingleThreadExecutor():创建单个线程的线程池
  • Executors.newCachedThreadPool():创建具有缓存功能的线程池
  • Executors.newScheduledThreadPool(coreSize):创建具有调度功能的线程池

2、案例

public class ExecutorsDemo {

    public static void main(String[] args) {
        // 创建固定大小的线程池
        final ExecutorService pool2 = Executors.newFixedThreadPool(2);
        // 创建单个线程的线程池
        final ExecutorService pool3 = Executors.newSingleThreadExecutor();
        // 创建具有缓存功能的线程池
        final ExecutorService pool4 = Executors.newCachedThreadPool();
        // 创建具有调度功能的线程池
        final ScheduledExecutorService pool5 = Executors.newScheduledThreadPool(2);

        // 定时任务
        pool5.scheduleAtFixedRate(() -> System.out.println("执行定时任务"), 1, 2, TimeUnit.SECONDS);

        // 延时任务
        pool5.schedule(() -> System.out.println("执行延时任务"), 3, TimeUnit.SECONDS);
    }

}

Fork & Join

1、概述

  • :将一个大任务拆分为算法上相同的小任务,直至不能拆分可以直接求解。它体现的是一种分治思想,适用于能够进行任务拆分的 cpu 密集型运算

2、案例

/**
 * 使用Fork & Join方式 拆分1-5之间的和
 */
public class ForkDemo {

    public static void main(String[] args) {
        ForkJoinPool pool = new ForkJoinPool();
        final Integer value = pool.invoke(new Task(1, 5));
        System.out.println("结果:" + value);
    }

}

class Task extends RecursiveTask<Integer> {

    private final int begin;

    private final int end;

    public Task(int left, int right) {
        this.begin = left;
        this.end = right;
    }

    @Override
    protected Integer compute() {
        if (begin == end) {
            return begin;
        }
        if (end - begin == 1) {
            return begin + end;
        }
        int mid = (end + begin) / 2;
        Task task1 = new Task(begin, mid);
        task1.fork();
        Task task2 = new Task(mid + 1, end);
        task2.fork();

        return task1.join() + task2.join();
    }

}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值