目录
线程的等待
线程的睡眠
等待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; } } //缓冲区 ArrayListstreams = 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线程池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一个提供创建线程池方法的工具类
主要创建线程池的几个方法
实现方法:
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.线程池(作用,有哪几种线程池,线程池的创建有哪些参数,如何优化,实现原理(看源码、画图、归纳))
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)