自定义线程池

线程池

自定义线程池的工作流程:

  • 线程池刚开始没有线程时,当一个任务提交给线程池后,线程池会创建出一个新的线程来执行任务
  • 当线程数达到coreSize时还没有线程空闲,这时再加入的任务就被放进队列中等待被空闲的线程执行
  • 线程池的目的就是让一个线程执行多个任务,达到线程的重复利用

步骤1:自定义任务队列

public class BlockedQueue<T> {
    //队列的容量
    private int capacity;
    //用来存放任务
    private Deque<T> taskQueue = new ArrayDeque<>();
    private ReentrantLock lock = new ReentrantLock();
    private Condition fullCondition = lock.newCondition();
    private Condition emptyCondition = lock.newCondition();

    public BlockedQueue(int capacity) {
        this.capacity = capacity;
    }
	//从任务队列中获取任务
    public T take() {
        lock.lock();
        try {
            while (taskQueue.isEmpty()) {
                try {
                    emptyCondition.await();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            T t = taskQueue.removeFirst();
            fullCondition.signalAll();   //只会唤醒所有的因为队列已满而陷入waiting的线程
            return t;
        } finally {
            lock.unlock();
        }
    }
	//将任务加入到队列中
    public void put(T t) {
        lock.lock();
        try {
            while (taskQueue.size() == capacity) {
                try {
                    fullCondition.await();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            taskQueue.push(t);
            emptyCondition.signalAll();
        } finally {
            lock.unlock();
        }
    }
}

步骤2:自定义线程池

class ThreadPool{

    private int coreSize;   //核心线程数
    private BlockedQueue<Runnable> taskQueue;   //存储任务
    private Set<Thread> threads = new HashSet<>();  //存储线程

    public ThreadPool(int coreSize,int capacity) {
        this.coreSize = coreSize;
        taskQueue = new BlockedQueue<>(capacity);
    }

    public void execute(Runnable task){
        synchronized (taskQueue) {
            if(threads.size() < coreSize){  //小于核心线程数时就创建新的线程并执行任务
                Worker worker = new Worker(task);
                worker.start();
                threads.add(worker);
            }else{                      //当大于核心线程数后不会创建新的线程,会把任务加入到任务队列,等待这些核心线程去轮流执行
                System.out.println("将任务加入队列中,此时线程量为:"+threads.size());
                taskQueue.put(task);
            }
        }
    }
  //将线程进行包装能够从任务队列中获取任务并执行
    class Worker extends Thread{
        private Runnable task;
        public Worker(Runnable task) {
            this.task = task;
        }
        @Override
        public void run() {
            while (task != null || (task = taskQueue.take()) != null){ //当执行完构造方法传来的任务后就从任务队列中不断的获取
                try {
                    task.run();
                } catch (Exception e){//当任务发生异常时捕获,保证线程可以正常执行
                    e.printStackTrace();
                }finally {
                    task = null;
                }
                //当循环结束后说明队列中的任务都已执行完毕,将当前线程this移出集合
                synchronized (taskQueue){
                    threads.remove(this);
                }
            }
        }
    }
}

测试

public static void main(String[] args) throws InterruptedException {
    ThreadPool threadPool = new ThreadPool(3,5);
    for(int i = 0; i < 5; i++){
        int j = i;
        threadPool.execute(()->{

            System.out.println("线程"+Thread.currentThread()+"执行了任务"+j);
        });
    }
}

image-20210127110535582

对线程池进行升级,加入拒绝策略。

步骤一:加入决绝策略的接口

@FunctionalInterface
interface RejectPolicy<T>{
    void reject(BlockedQueue<T> queue,T task);
}

步骤二:给任务队列增加新的具有限时功能方法,poll()和offer()。

​ 在线程池中使用拒绝策略,当任务队列已满时按照用户自定义的方式处理任务。

import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;

public class BlockedQueue<T> {
    //队列的容量
    private int capacity;
    //用来存放任务
    private Deque<T> taskQueue = new ArrayDeque<>();
    private ReentrantLock lock = new ReentrantLock();
    private Condition fullCondition = lock.newCondition();
    private Condition emptyCondition = lock.newCondition();

    public BlockedQueue(int capacity) {
        this.capacity = capacity;
    }
    public T take() {
        lock.lock();
        try {
            while (taskQueue.isEmpty()) {
                try {
                    emptyCondition.await();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            T t = taskQueue.removeFirst();
            fullCondition.signalAll();   
            return t;
        } finally {
            lock.unlock();
        }
    }
    //加入超时获取,超时后返回null
    public T poll(long timeout, TimeUnit timeUnit){
        lock.lock();
        try {
            long nanos = timeUnit.toNanos(timeout);
            while (taskQueue.isEmpty()) {
                try {
                    if(nanos < 0){	
                        return null;
                    }
                    nanos = emptyCondition.awaitNanos(nanos); //返回剩余时间
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            T t = taskQueue.removeFirst();
            fullCondition.signalAll();
            return t;
        } finally {
            lock.unlock();
        }
    }
    public void put(T t) {
        lock.lock();
        try {
            while (taskQueue.size() == capacity) {
                try {
                    fullCondition.await();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            taskQueue.push(t);
            emptyCondition.signalAll();
        } finally {
            lock.unlock();
        }
    }
		//再给定时间内将任务添加到队列中,如果失败返回false。
    public boolean offer(T t,long timeout,TimeUnit timeUnit){
        lock.lock();
        try {
            long nanos=  timeUnit.toNanos(timeout);
            while (taskQueue.size() == capacity) {
                try {
                    if(nanos < 0) {
                        return false;
                    }
                    nanos = fullCondition.awaitNanos(nanos);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            taskQueue.push(t);
            emptyCondition.signalAll();
            return true;
        } finally {
            lock.unlock();
        }
    }
	//当任务数大于队列的容量时根据RejectPloicy接口规定的方式处理任务
    public void tryput(RejectPolicy rejectPolicy,T task){
            lock.lock();
        try {
            if(taskQueue.size() == capacity) {
                rejectPolicy.reject(this, task);
            }else{
                taskQueue.addLast(task);
                emptyCondition.signalAll();
            }
        } finally {
            lock.unlock();
        }
    }
}
class ThreadPool{
    private int coreSize;   //核心线程数
    private BlockedQueue<Runnable> taskQueue;   //存储任务
    private Set<Thread> threads = new HashSet<>();  //存储线程
    private long timeout;
    private TimeUnit timeUnit;
    private RejectPolicy<Runnable> rejectPolicy;
    public ThreadPool(int coreSize,int capacity,long timeout,TimeUnit timeUnit,RejectPolicy<Runnable> rejectPolicy) {
        this.coreSize = coreSize;
        taskQueue = new BlockedQueue<>(capacity);
        this.timeout = timeout;
        this.timeUnit = timeUnit;
        this.rejectPolicy = rejectPolicy;
    }

    public void execute(Runnable task){
        synchronized (taskQueue) {
            if(threads.size() < coreSize){  
                Worker worker = new Worker(task);
                worker.start();
                threads.add(worker);
            }else{                   
                System.out.println("将任务加入队列中,此时线程量为:"+threads.size());
                taskQueue.tryput(rejectPolicy,task);
            }
        }
    }
    class Worker extends Thread{
        private Runnable task;
        public Worker(Runnable task) {
            this.task = task;
        }
        @Override
        public void run() {
            while (task != null || (task = taskQueue.poll(timeout,timeUnit)) != null){ 
                try {
                    task.run();
                } catch (Exception e){行
                    e.printStackTrace();
                }finally {
                    task = null;
                }
                synchronized (taskQueue){
                    threads.remove(this);
                }
            }
        }
    }
}

测试

public static void main(String[] args) throws InterruptedException {
        ThreadPool threadPool = new ThreadPool(3,5,1000,TimeUnit.MILLISECONDS,(queue,task)->{
            task.run();	//当任务队列已满时主线程自己调用该任务
        });
        for(int i = 0; i < 10; i++){
            int j = i;
            threadPool.execute(()->{

                System.out.println("线程"+Thread.currentThread()+"执行了任务"+j);
            });
        }
    }

image-20210127124230096

ThreadPoolExecutor

1)线程状态

原子变量ctl的高3位表示线程状态,低29位表示数量。可以用一次cas操作赋值

2)构造方法
public ThreadPoolExecutor(int corePoolSize,	//核心线程数
                          int maximumPoolSize,	//最大线程数:核心数+救急线程数
                          long keepAliveTime,	//救急线程存活时间
                          TimeUnit unit,	//时间单位
                          BlockingQueue<Runnable> workQueue,	//阻塞队列:存储任务
                          RejectedExecutionHandler handler	//拒绝策略
                         )
  • 线程池刚开始没有线程时,当一个任务提交给线程池后,线程池会创建出一个新的线程来执行任务
  • 当线程数达到coreSize时还没有线程空闲,这时再加入新的任务就被加入我认可Queue队列排队,直到被空闲的线程执行
  • 如果选择有界队列,当任务数超过队列大小时会创建maximumPoolSize-corePoolSize个线程来救急
  • 如果线程数达到maximunPoolSize仍然有新任务这时会采用决绝策略,jdk共提供了4种实现
  • 当救急线程在高峰期过去后空闲keepAliveTime时间后就会结束
3)newFixedThreadPool
public static ExecutorService newFixedThreadPool(int nThreads) {
    return new ThreadPoolExecutor(nThreads, nThreads,
                                  0L, TimeUnit.MILLISECONDS,
                                  new LinkedBlockingQueue<Runnable>());
  • 核心线程数 == 最大线程数,表示不会创建救急线程,因此 keepAliveTime是0。
  • 使用无界队列,可以存放任意数量的任务。
  • 适合任务量已知,相对耗时的任务。
4)newCachedThreadPool
public static ExecutorService newCachedThreadPool() {
    return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                  60L, TimeUnit.SECONDS,
                                  new SynchronousQueue<Runnable>());
}
  • 核心线程数是0,所有的线程都是救急线程,最大数量为Integer.MAX_VALUE,存活时间是60‘s,
  • 队列采用了SynchronousQueue,它没有容量,只有一个线程来取得时候另一个线程才能放任务
  • 适合任务数密集,但每个任务执行时间短得情况
5)newSingleThreadExecutor
public static ExecutorService newSingleThreadExecutor() {
    return new FinalizableDelegatedExecutorService
        (new ThreadPoolExecutor(1, 1,
                                0L, TimeUnit.MILLISECONDS,
                                new LinkedBlockingQueue<Runnable>()));
}
  • 只有一个核心线程,当任务数多余1时会放进队列中排队等待让这一个线程执行。
  • 如果自己创建一个单线程串行执行任务,线程发生异常而终止那么其他任务会无法被执行。而线程池还会新建一个线程保证正常工作。

线程池中的线程都是非守护线程,不会随着主线程结束而结束

异步模式之工作线程

有限的线程轮流执行无限多的任务,典型实现是线程池,是享元模式的体现。

不同任务类型应该使用不同的线程池,可以避免饥饿,提升效率。

1、饥饿的例子
  • 两个员工是同一线程池中两个线程
  • 他们处理的任务是:
    • 客人点餐:必须先点完餐才能上菜
    • 后厨上菜:上菜完毕后整个任务才算结束
  • 如果只有两个线程,当两个线程分别去处理客人A和客人B的点餐,那么上菜没有线程去处理,两个任务都无法结束,饥饿。
public class S1HungryLock {
    static final List<String> cookies = Arrays.asList("地三鲜", "黄焖鸡", "牛肉面", "汉堡");
    static Random random = new Random(cookies.size());

    static String cooking() {
        return cookies.get(random.nextInt(cookies.size()));
    }

    public static void main(String[] args) throws InterruptedException {
        ExecutorService executorService = Executors.newFixedThreadPool(2);
        //第一位客人
        executorService.execute(() -> {
            System.out.println("给客人A处理点餐");
            Future<String> f = executorService.submit(() -> {
                System.out.println("给客人A做菜");
                return cooking();
            });
            try {
                System.out.println("给客人A上菜:" + f.get());
            } catch (InterruptedException e) {
                e.printStackTrace();
            } catch (ExecutionException e) {
                e.printStackTrace();
            }
        });
      //第二位客人
        executorService.execute(() -> {
            System.out.println("给客人B处理点餐");
            Future<String> f = executorService.submit(() -> {
                System.out.println("给客人B做菜");
                return cooking();
            });
            try {
                System.out.println("给客人B上菜:" + f.get());
            } catch (InterruptedException e) {
                e.printStackTrace();
            } catch (ExecutionException e) {
                e.printStackTrace();
            }
        });

    }
}

image-20210126162236187

两个任务都无法结束,解决方法是不同的线程池处理不同的任务

ExecutorService executorService1 = Executors.newFixedThreadPool(1);
ExecutorService executorService2 = Executors.newFixedThreadPool(1);
//第一位工人
executorService1.execute(() -> {
    System.out.println("给客人A处理点餐");
    Future<String> f = executorService2.submit(() -> {
        System.out.println("给客人A做菜");
        return cooking();
    });
    try {
        System.out.println("给客人A上菜:" + f.get());
    } catch (InterruptedException e) {
        e.printStackTrace();
    } catch (ExecutionException e) {
        e.printStackTrace();
    }
});
executorService1.execute(() -> {
    System.out.println("给客人B处理点餐");
    Future<String> f = executorService2.submit(() -> {
        System.out.println("给客人B做菜");
        return cooking();
    });
    try {
        System.out.println("给客人B上菜:" + f.get());
    } catch (InterruptedException e) {
        e.printStackTrace();
    } catch (ExecutionException e) {
        e.printStackTrace();
    }
});

image-20210126162546689

任务调度线程池

Timer只有一个线程串行执行任务,当线程发生异常时(i/0)会导致其他任务无法执行,可以使用ScheduleExecutorService。

ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(2);	//使用两个线程执行定时任务
//1秒后执行任务一
scheduledExecutorService.schedule(()->{
    System.out.println("任务一,执行时间:"+new Date());
    try {
        Thread.sleep(1000);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
},1000, TimeUnit.MILLISECONDS);
//2秒后执行任务二
scheduledExecutorService.schedule(()->{
    System.out.println("任务二,执行时间:"+new Date());
},2000, TimeUnit.MILLISECONDS);

ForkJoinPool线程池

是JDK1.7加入的线程池实现,体现出分治的思想,适用于进行任务拆分的cpu密集型运算,把每个任务的分解和合并交给不同的线程来完成,提升运算效率。会默认创建与cpu核心数大小相同的线程池。

@Slf4j(topic = "c.add")
class AddTask extends RecursiveTask<Integer>{
    private int n;
    public AddTask(int n){
        this.n = n;
    }

    @Override
    public String toString() {
        return "{"+n+"}";
    }

    @Override
    protected Integer compute() {
        if(n == 1) {
            log.debug("join(),{}",n);
            return 1;
        }
        //拆封结果
        AddTask t = new AddTask(n-1);
        t.fork();
        log.debug("fork() {}+{}",n,t);
        //合并结果
        int result = n + t.join();
        log.debug("join() {}+{}={}",n,t,result);
        return result;
    }

    public static void main(String[] args) {
        ForkJoinPool pool = new ForkJoinPool();
        pool.invoke(new AddTask(5));
    }
}

image-20210126172517279

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
线程池自定义线程池工厂指的是我们可以通过自定义工厂类来创建线程池。在Java中,我们可以通过实现ThreadFactory接口来自定义线程池工厂。通过自定义工厂类,我们可以定制线程的创建方式,例如给线程设置特定的命名规则、设置线程的优先级等。 自定义线程池工厂的步骤如下: 1. 创建一个实现ThreadFactory接口的自定义工厂类,并实现其`newThread(Runnable r)`方法。 2. 在`newThread`方法中,我们可以通过`Thread`类的构造方法来创建线程,并进行一些定制化的操作,比如设置线程的名称、优先级等。 3. 自定义线程池工厂类的实例化后,我们可以将其作为参数传递给线程池创建方法中,以便使用自定义线程池工厂来创建线程池。 举个例子,假设我们需要自定义线程池工厂来创建线程池,可以按照以下步骤进行: 1. 创建一个自定义线程池工厂类,例如`CustomThreadFactory`,并实现ThreadFactory接口。 2. 在`CustomThreadFactory`类中,实现`newThread(Runnable r)`方法,并在该方法中创建线程,并设置线程的名称。 3. 在使用线程池的地方,例如`Executors.newFixedThreadPool()`方法中,将`CustomThreadFactory`类的实例传递给`newFixedThreadPool()`方法,以使用自定义线程池工厂来创建线程池。 通过自定义线程池工厂,我们可以更加灵活地控制线程的创建过程,并根据实际需求进行定制化操作。这样可以提高线程池的灵活性和可扩展性,使其更好地适用于各种场景的需求。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值