初学线程,线程池

线程的等待和通知

Oject类中包含的方法

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

注意:调用等待和通知方法的对象必须是锁对象

wait和sleep方法的区别

  • 调用对象不同

        wait方法由锁对象调用

        sleep方法由线程调用

  • 锁使用不同

        执行wait方法后,自动释放锁

        执行sleep方法后,不会释放锁

  • 唤醒机制不同

        执行wait方法后,可以被通知唤醒

        执行sleep后,只能等待时间结束后,自动唤醒


生产者消费者设计模式

  • 生产者

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

  • 消费者

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

生产者与消费者间的问题

  • 耦合性高,生产者与消费者之间联系紧密,不利于系统的维护与扩展
  • 并发性能低,同时能处理的请求量少
  • 忙闲不均,生产者和消费者的(生产/消费)速度不一致,会带来系统资源的浪费

 实现过程:

  1. 通过添加缓冲区,设置上限
  2. 生产者生产数据,向缓冲区存放,如果满了,生产者进入等待,直到缓冲区有空的位置再通知生产者生产
  3. 消费者取出数据进行消费,如果缓冲区空了,消费者进入等待,直到缓冲区有数据了再通知消费者消费

解决了忙闲不均的问题,提高了并发性和降低了能耦合性

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 product() throws InterruptedException {
        if (baozis.size() == MAX_COUNT){
            System.out.println("包子太多了");
            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 eat() throws InterruptedException {
        if (baozis.size() == 0){
            System.out.println("包子没了,等待中");
            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();
        //1个生产者
        Thread productor = new Thread(() -> {
            for (int i = 0; i < 100; i++) {
                try {
                    baoziShop.product();

                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        //10个消费者吃10个包子
        for (int i = 0; i < 10; i++) {
            Thread consumer = new Thread(() ->{
                try {
                    for (int j = 0; j < 10; j++) {
                        baoziShop.eat();
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            });
            consumer.start();
        }
        productor.start();
    }

阻塞队列

应用了生产者消费者设计模式的集合,能根据数据的情况,自动对线程执行等待和通知命令

BlockingQueue接口

  • put 添加数据,达到上限会自动让线程等待
  • take 取,并删除数据,数据空了会自动让线程等待

实现类

ArrayBlockingQueue类 数据结构为数组

LinkedBlockingQueue类 数据结构为链表

static class Baozi{
        private int id;

        public Baozi(int id) {
            this.id = id;
        }

        @Override
        public String toString() {
            return "包子" + id;
        }
    }


    public static void main(String[] args) {
        //阻塞队列
        ArrayBlockingQueue<Baozi> baozis = new ArrayBlockingQueue<>(100);
        //生产者线程
        new Thread(() -> {
            for (int i = 0; i < 200; i++) {
                Baozi baozi = new Baozi(baozis.size() + 1);
                try {
                    baozis.put(baozi);
                    System.out.println(Thread.currentThread().getName() + "生产了" + baozi);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }).start();
        for (int i = 0; i < 5; i++) {
            new Thread(() -> {
                //取包子
                for (int j = 0; j < 40; j++) {
                    try {
                        Baozi take = baozis.take();
                        System.out.println(Thread.currentThread().getName() +"消费了" + take);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }).start();
        }
    }

线程池

作用:回收线程资源

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

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

线程池的使用

继承Executor

添加线程管理方法,如:shutdown()、shutdownNow()

Executors

用于创建线程池的工具类

主要方法

方法名说明
newCachedThreadPool()创建长度不限的线程池
newFixedThreadPool(int)创建长度固定的线程池
newSingleThreadExecutor()创建长度为一的线程
newScheduledThreadPool(int)创建固定长度的可以定时,周期性地执行任务的线程
public static void cachedThreadPool() throws InterruptedException {
        //创建不限长度的线程池
        ExecutorService executorService = Executors.newCachedThreadPool();
        for (int i = 0; i < 1000; i++) {
            int n = i;
            //使用线程池启动线程
            executorService.execute(() -> {
                System.out.println(Thread.currentThread().getName() + "执行了任务" + n);
            });
            Thread.sleep(1);
        }
        executorService.shutdown();
    }

    public static void fixedThreadPool() throws InterruptedException {
        //创建长度固定的线程池
        ExecutorService executorService = Executors.newFixedThreadPool(5);
        for (int i = 0; i < 10; i++) {
            int n = i;
            //使用线程池启动线程
            executorService.execute(() -> {
                System.out.println(Thread.currentThread().getName() + "执行了任务" + n);
            });
            Thread.sleep(1);
        }
        executorService.shutdown();
    }

    public static void singleThreadPool() throws InterruptedException {
        //创建单一长度的线程池
        ExecutorService executorService = Executors.newSingleThreadExecutor();
        for (int i = 0; i < 100; i++) {
            int n = i;
            //使用线程池启动线程
            executorService.execute(() -> {
                System.out.println(Thread.currentThread().getName() + "执行了任务" + n);
            });
            Thread.sleep(1);
        }
        executorService.shutdown();
    }

    public static void scheduledThreadPool() throws InterruptedException {
        //获得可调度的线程池
        ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(5);
        //执行可调度任务
        System.out.println("------------");
        scheduledExecutorService.scheduleAtFixedRate(()->{
            System.out.println(Thread.currentThread().getName()+"---->"+ LocalDateTime.now());
        },1,3, TimeUnit.SECONDS);
    }


    public static void main(String[] args) throws InterruptedException {
//        cachedThreadPool();
//        fixedThreadPool();
//        singleThreadPool();
        scheduledThreadPool();

    }

 线程池的优化配置

线程池的实现类:ThreadPoolExecutor

线程池的构造方法参数:

  • corePoolsize 核心线程数,创建线程池时创建的,不会销毁

  • maximumPoolSize 最大线程数

  • keepAliveTime 线程存活时间,非核心线程空闲状态的时间,超过后销毁

  • timeUnit 时间单位

  • blockingQueue 阻塞队列,存放任务的集合

  • threadFactory 线程池创建线程使用的工厂

  • handler 线程池对拒绝任务的处理

优化配置

  1. 核心线程数应该与CPU内核数量相关,保证每个CPU都有执行1个或多个线程 CPU内核数*N(N和任务执行需要的时间和并发量相关)
  2. 最大线程数可以和核心线程数一样,避免频繁创建和小回县城
  3. 如果存在非核心线程,设置大一点,避免频繁创建和销毁线程
  4. 阻塞队列使用LinkedBlockingQueue,插入和删除任务效率更高

线程池的实现原理

思考:线程池是如何对线程进行回收利用的

所有的线程都保存在set集合中

当调用execute方法后,会判断当前线程数是否小于核心线程数,如果小于就会创建核心线程(执行addWorker方法),否则将任务添加到阻塞队列workQueue,如果阻塞队列满了,就创建非核心线程执行任务,如果线程创建失败(在addWorker方法中创建非核心线程时,如果当前线程大于最大线程数就返回false)就执行设置的拒绝方法

addWorker()方法会创建Worker对象存入HashSet集合,然后执行线程(runWorker)

在runWorker()会循环调用getTask()方法从阻塞队列中读取任务,只要getTask方法不返回null(即队列不为空),该线程就不会退出

  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值