线程池原理

线程的等待和通知

        Object类中的方法

wait()让当前线程进入等待状态,直到被通知为止
wait(long)让当前线程进入等待状态,同时设置时间,一直到被通知为止或者时间结束
notify()随机通知一个等待线程(随机无顺序)
notifyAll()通知所有的等待线程

        需要注意的点:等待和通知方法必须是锁对象,否则会抛出IllegalMonitorStateException

         例子:通过锁对象将线程等待,经过5秒通知该线程来执行

/**
 * 通过锁对象将线程等待,经过5秒通知该线程来执行
 */
public class WaitDemo {

    public synchronized void print() throws InterruptedException {
        for (int i = 0; i < 100; i++) {
            System.out.println(Thread.currentThread().getName() + "--->" +i);
            if(i == 50){
                //让当前线程等待
                this.wait();
            }
        }
    }

    public synchronized void notifyTest(){
        //让等待的线程执行
        this.notifyAll();
    }

    public static void main(String[] args) {
        WaitDemo waitDemo = new WaitDemo();
        new Thread(()->{
            try {
                waitDemo.print();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }).start();
        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        waitDemo.notifyTest();
    }
}

wait()和sleep()的区别 

调用对象不同锁使用不同唤醒机制不同
wait()由锁对象调用执行wait后 自动释放锁执行wait后,可以被唤醒通知
sleep()由线程调用执行sleep后 不会释放锁执行sleep后,只能等待时间结束后,自动唤醒

生产者消费者模式 (一种设计模式,不属于GOF23)

        生产者和消费者:

                生产者:某些程序、进程、线程负责生产数据就属于生产者

                消费者:某些程序、进程、线程负责使用数据就属于消费者

        生产者和消费者之间的问题:

                1.耦合性高,生产者和消费者联系紧密,不利于系统的维护和拓展

                2.并发性低,同时能处理的请求量少

                3.忙闲不均,生产者和消费者的速度不一致,带来系统资源的浪费

        实现的过程:(就像包子铺,生产者把包子放入笼屉中,消费者不必与生产者密切联系,而是通过第三者进行操作)

                1.通过添加缓冲区,设置上限

                2.生产者生产出数据,放入缓存区,如果满了 则生产者进入等待,直到缓冲区有空的位置通知生产者生产;

                3.消费者从缓冲区取数据进行消费,如果空了,消费者进入等待,直到缓冲区有数据再通知消费者消费。

        解决问题:

                1.解耦

                2.提高并发性能

                3.解决,忙闲不均

包子铺例子:

/**
 * 包子铺
 */
public class BaoziShop {
    /**
     * 包子
     */
    class Baozi{
        private int id;
        public Baozi(int id) {
            this.id = id;
        }

        @Override
        public String toString() {
            return "包子--" + id;
        }
    }
    //上限
    public static final int MAX_COUNT = 100;
    //缓冲区 存放数据
    private List<Baozi> baozis = new ArrayList<>();

    /**
     * 做包子
     */
    public synchronized void makeBaozi() throws InterruptedException {
        //判断缓冲区是否满了
        if(baozis.size() == MAX_COUNT){
            System.out.printf("缓冲区满了,%s等待%n",Thread.currentThread().getName());
            //让生产者线程等待
            this.wait();
        }else{
            //通知生产者线程生产
            this.notifyAll();
        }
        //创建包子
        Baozi baozi = new Baozi(baozis.size() + 1);
        System.out.println(Thread.currentThread().getName()+"做了"+baozi);
        //保存到缓冲区
        baozis.add(baozi);
    }

    /**
     * 拿包子
     */
    public synchronized void takeBaozi() throws InterruptedException {
        //判断缓冲区是否空了
        if(baozis.size() == 0){
            System.out.printf("缓冲区空了,%s等待%n", Thread.currentThread().getName());
            //让消费者等待
            this.wait();
        }else{
            //通知消费者消费
            this.notifyAll();
        }
        //获得第一个包子,并删除
        if(baozis.size() > 0){
            Baozi baozi = baozis.remove(0);
            System.out.println(Thread.currentThread().getName()+"吃了"+baozi);
        }
    }

    public static void main(String[] args) {
        BaoziShop baoziShop = new BaoziShop();
        //一个生产者
        Thread productor = new Thread(() -> {
            for (int i = 0; i < 100; i++) {
                try {
                    baoziShop.makeBaozi();
                    Thread.sleep(1);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        productor.start();
        //10个消费者吃10个包子
        for (int i = 0; i < 10; i++) {
            Thread consumer = new Thread(() ->{
                try {
                    for (int j = 0; j < 10; j++) {
                        baoziShop.takeBaozi();
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            });
            consumer.start();
        }
    }
}

阻塞队列

        应用了生产者消费者模式的集合,能够根据满或者空的情况,自动对线程执行等待和通知

         BlockingQueue接口:

                        put添加数据,达到上限会自动让线程等待

                        take取并删除数据,数据空了会自动让线程等待

        实现类:

                        ArrayBlockingQueue 类 数据结构为数组

                        LinkedBlockingQueue类 链表结构

线程池:

        作用:

                线程是一种宝贵的系统资源,执行完任务后会死亡,如果有大量任务需要处理,需要频繁的创建和销毁线程,造成系统性能降低。

                线程池会保存一定量的线程,线程执行完任务后,会回到线程池中,等待下一个任务,节省系统资源,提升性能。

线程池的使用: (顶层接口:Executor)

        executor(Runnable)   启动线程执行一个任务

ExecutorService(用于创建线程池的工具类 )

        主要的方法:

方法名说明
newCachedThreadPool()创建长度不限的线程池
newFixedThreadPool(int )创建固定长度的线程池
newSingleThreadExecutor()创建单一个数的线程池
newScheduledThreadPool(int)创建可以调度的线程池

        

线程池的优化配置

        线程池的实现类(ThreadPoolExecutor)

                线程池的构造方法参数:

                        corePoolSize 核心线程数,创建线程池后自带线程,不会进行销毁

                        maximumPoolSize 最大线程数

                        keepAliveTime 存活时间,非核心线程能够闲置的时间,超过后被销毁

                        timeUnit 时间单位

                        blockingQueue 阻塞队列 存放任务(Runnable)的集合

优化配置

  1. 核心线程数 应该和CPU内核数量相关 CPU内核数 * N (N和任务执行需要时间和并发量相关)

    Runtime.getRuntime().availableProcessors()
  2. 最大线程数可以和核心线程数一样,避免频繁创建和销毁线程

  3. 如果存在非核心线程,设置大一点,避免频繁创建和销毁线程

  4. 阻塞队列使用LinkedBlockingQueue,插入和删除任务效率更高

线程池的实现原理     

        1、判断线程池里的核心线程是否都在执行任务,如果不是(核心线程空闲或者还有核心线程没有被创建)则创建一个新的工作线程来执行任务。如果核心线程都在执行任务,则进入下个流程。

        2、线程池判断工作队列是否已满,如果工作队列没有满,则将新提交的任务存储在这个工作队列里。如果工作队列满了,则进入下个流程。

        3、判断线程池里的线程是否都处于工作状态,如果没有,则创建一个新的工作线程来执行任务。如果已经满了,则交给饱和策略来处理这个任务。

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值