线程的等待和睡眠,线程池创建和使用

目录

线程的等待

线程的睡眠

等待wait()和睡眠sleep()区别

实现案例:让一个线程进入等待状态,五秒后由另一个线程唤醒

生产者和消费者模式

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

阻塞队列

 线程池

线程池的使用

顶层接口:executor

executors

线程池的优化配置

优化配置

线程池实现原理

首先通过构造方法创建一个线程池

 然后通过execute方法判断核心线程数是否大于线程数,大于创建非核心线程,小于创建非核心线程

 然后通过addWorker方法,创建任务加入HashSet(所有任务都保存在HashSet中)

 通过runWorker执行任务 循环调用 getTask得到任务,task.run()执行任务

如果队列为空就进入阻塞。

总结


线程的等待

Object类中的方法

wait(),让当前线程进入等状态,直到被通知。

wait(long),设置等待时间,知道被通知或者等待时间结束。

notify,通知,随机通知一个线程

notifyAll(),通知所有线程

线程的睡眠

sleep(时间毫秒),让线程睡眠一段时间,时间结束后自动唤醒。

等待wait()和睡眠sleep()区别

wait()由锁对象调用,sleep()由当前线程调用。

wait()可以被其他线程通知唤醒,sleep()只能等待睡眠时间结束。

wait()等待会释放锁,sleep()不会释放锁。

实现案例:让一个线程进入等待状态,五秒后由另一个线程唤醒

public class AAA {
    //执行线程
    public synchronized void threadA(){
            System.out.println("线程A开始执行");
            for (int i = 0; i <100 ; i++) {
                if(i==50){
                    try {
                        System.out.println("线程进入等待");
                        this.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                System.out.println("执行了"+i);
            }
    }
    //唤醒线程
    public synchronized void threadB(){
            System.out.println("唤醒线程");
            this.notify();
    }

    public static void main(String[] args) {
        AAA aaa = new AAA();
        new Thread(()->{
            aaa.threadA();
        }).start();
        try {
            Thread.sleep(5000);
            aaa.threadB();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

生产者和消费者模式

        生产者:某些程序/进程/线程创建数据,就是生产者。

        消费者:某些程序/进程/线程消耗数据,就是消费者。

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

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

        忙闲不均,生产者和消费者速度不一致,导致系统资源浪费。

        并发性能低,同时能处理的请求量小。

例如:包子铺,老板(生产者)做一个包子递给客户(消费者),没有客户就等待不做。当多个客户来买包子得等待。

解决方案:加一个缓冲区(蒸笼)。老板可以提前做一定数量的包子放进蒸笼,不用等待客户来。客户也可以直接从蒸笼拿,不用等老板做。当蒸笼没有包子在通知老板做。

案例实现:

import java.util.ArrayList;

public class StreamDemo2 {
    //包子上限
    public static final int MAX_BAO = 100;

    class Baozi {
        private int id;

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

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


    //缓冲区
    ArrayList<Baozi> streams = new ArrayList<>(MAX_BAO);

    //生产包子
    public synchronized void makeBao() {
        if (streams.size() == 100) {
            try {
                //生产者进入等待
                System.out.println(Thread.currentThread().getName()+"包子数量到达上线,老板进入等待");
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        } else {
            //通知生产者做包子
            this.notifyAll();
        }
        Baozi baozi = new Baozi(streams.size()+1);
        streams.add(baozi);
        System.out.println("做了" + baozi);
    }

    //消费包子
    public synchronized void shopBao() {
        if (streams.size() == 0) {
            System.out.println("包子买完,顾客进入等待");
            try {
                //消费者进入等待
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        } else {
            //通知消费者拿包子
            this.notifyAll();
        }
        if(streams.size()>0){
            Baozi baozi = streams.remove(0);
            System.out.println("顾客消费了" + baozi);
        }
    }

    public static void main(String[] args) {
        StreamDemo2 streamDemo2 = new StreamDemo2();
        //一个线程做包子
        new Thread(() -> {
            for (int i = 0; i < 100; i++) {
                streamDemo2.makeBao();
                try {
                    Thread.sleep(1);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }).start();
        //十个线程来买包子
        for (int i = 0; i < 10; i++) {
            new Thread(() -> {
                //一个线程买十个包子
                for (int j = 0; j <10 ; j++) {
                    streamDemo2.shopBao();
                }
            }).start();
        }
    }
}

上面我们是通过手动让线程等待和通知,太过麻烦。有没有简便的方式呢?

阻塞队列

应用了生产者和消费者模式的集合,可以根据数据的状态自动帮我们对线程执行等待或通知。

BlockingQueue 接口

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

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

实现类

ArrayBlockingQueue 类 数据结构为数组

LinkedBlockingQueue类 链表结构

用ArrayBlockingQueue实现上面案例:

import java.util.concurrent.ArrayBlockingQueue;

public class SteamDemo {

    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> queue = new ArrayBlockingQueue<>(100);

        new Thread(()->{
            for (int i = 0; i < 200; i++) {
                Baozi baozi = new Baozi(queue.size()+1);
                try {
                    queue.put(baozi);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("生产了"+baozi);
            }
        }).start();

        for (int i = 0; i <5 ; i++) {
            new Thread(()->{

                for (int j = 0; j <40 ; j++) {
                    try {
                        Baozi baozi=queue.take();
                        System.out.println("吃了"+baozi);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }).start();
        }
    }
}

 线程池

         线程,是一种宝贵的资源,每个线程执行完指令就会销毁。如果有大量的任务需要处理,那么大量的线程创建和销毁会对资源消耗非常严重。

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

线程池的使用

顶层接口:executor

方法:execute(Runnalbe)

继承executor

添加线程池管理方法,如:shutdown,shoudownNow

executors

一个提供创建线程池方法的工具类

主要创建线程池的几个方法

newCachedTreadPool()创建长度不限的线程池
newFixedThreadPool (int)创建固定长度的线程池
newSingleThreadExecutor()创建单一个数的线程
newScheduledThreadPool(int)创建可以调度的线程

实现方法:

import java.time.LocalDateTime;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

public class ThreadPool {
    //创建长度不限的线程池
   public void poolA(){
       ExecutorService service = Executors.newCachedThreadPool();
       for (int i = 0; i <10 ; i++) {
           int n =i;
           service.execute(()->{
               System.out.println(Thread.currentThread().getName()+"执行了任务"+n);
           });
       }
       service.shutdown();
   }
    //创建固定长度线程池
    public void poolB(){
       ExecutorService service = Executors.newFixedThreadPool(2);//线程池里的线程数
        for (int i = 0; i <10 ; i++) {
            int n =i;
            service.execute(()->{
                System.out.println(Thread.currentThread().getName()+"执行了任务"+n);
            });
        }
        service.shutdown();
    }
    //创建单一个数线程池
    public void poolC(){
       ExecutorService service = Executors.newSingleThreadExecutor();
        for (int i = 0; i <10 ; i++) {
            int n =i;
            service.execute(()->{
                System.out.println(Thread.currentThread().getName()+"执行了任务"+n);
            });
        }
        service.shutdown();
    }
    //创建可调度的线程
    public void poolD(){
        ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(10);
        //调度的线程,一秒后执行,三秒执行依次,时间单位秒
        scheduledExecutorService.scheduleAtFixedRate(()->{
            System.out.println(Thread.currentThread().getName()+"执行了"+ LocalDateTime.now());},1,3, TimeUnit.SECONDS);
        }
    public static void main(String[] args) {
        new ThreadPool().poolD();
    }
}

线程池的优化配置

首先。来了解线程池构造方法里参数的含义:

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

maximunPoolSize 最大线程数

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

timeUnit 时间单位

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

 

优化配置

核心线程数可以设置为cpu内核*N(N和任务执行需要时间和并发量相关)

最大线程数可以设置为核心线程数,避免线程频繁的创建和销毁

如果非核心线程存在,可以将核心线程数设置大一点

阻塞队列用LinkedBlockingQueue,链表增删快

线程池实现原理

线程池是对线程的回收利用,不让其销毁,那线程池是怎么做到的呢?

可以看底层源码:

首先通过构造方法创建一个线程池

 然后通过execute方法判断核心线程数是否大于线程数,大于创建非核心线程,小于创建非核心线程

 然后通过addWorker方法,创建任务加入HashSet(所有任务都保存在HashSet中)

 通过runWorker执行任务 循环调用 getTask得到任务,task.run()执行任务

如果队列为空就进入阻塞。

 更多细节可以去看看源码,这里只截图了一部分。

总结

1.线程的等待和通知(是干什么,谁来调用)

2.wait和sleep的区别

3.生产者和消费者模式(是什么,解决什么问题,如何实现的,项目中有哪些应用)

4.阻塞队列(了解)

5.线程池(作用,有哪几种线程池,线程池的创建有哪些参数,如何优化,实现原理(看源码、画图、归纳))

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值