1.线程通信概念
多个线程在处理同一个资源,并且任务不同时,需要线程通信来帮助解决线程之间对同一个变量的使用或操作。就是多个线程在操作同一份数据是,避免对同一类共享变量的争夺。
于是我们引出了,等待唤醒机制。
2.等待唤醒机制
Wait( ):无线等待
wait(long time):定时等待
notify( ):通知一个人结束等待
notifyAll( ):通知所有人结束等待
单生产者单消费者:notify + if/while
多生产者多消费者:notifyAll + while
3.notifyAll用法
调用wait和notify方法需要注意的细节
1. wait方法与notify方法必须要由同一个锁对象调用。因为:对应的锁对象可以通过notify唤醒使用同一个锁对
象调用的wait方法后的线程。
2. wait方法与notify方法是属于Object类的方法的。因为:锁对象可以是任意对象,而任意对象的所属类都是继
承了Object类的。
3. wait方法与notify方法必须要在同步代码块或者是同步函数中使用。因为:必须要通过锁对象调用这2个方
法。
4.生产者与消费者关系(吃包子与做包子)
两个是包子的与两个做包子的(多生产与多消费)
为什么在多生产多消费中,用while而不是if ?
因为在多生产者与多消费者模式中,第一个消费者线程结束之后,如果用if的话,就直接下去进行第二线程的调用,但是却不会进行对第二个线程生产的再次判断,想要进行再次的判断,也就是说包子有没有再生产出一个,就必须再进行一次的判断,所以必须用while循环。
下面是单生产单消费的模式:
(1)下面两图是吃包子的消费者:
(2)下面两图片是生产包子的两图片:
(3)创建的两个线程
(4)下面是多生产与多消费:相比仅仅改变了一下线程数量,以及循环结构
线程池
其实就是一个容纳多个线程的容器,其中的线程可以反复使用,省去了频繁创建线程对象的操作,无需反复创建线程而消耗过多资源。
1.java线程池的运行原理
有限的线程重复利用,通过队列执行多个任务。
2.线程的限制
在Java9 64位,一个线程大约1mb,所以必须限制创造线程。
与线程池相关的东西都在java.util.concurrent包中。
Executor是线程池的顶级接口,只负责执行任务。
ExecutorService是子接口,也可以执行任务,还有线程池的管理功能。
Executors是线程池相关的工具类,一般用这个创建线程池。
3.线程池的使用步骤
1. 通过工具类Executors创建一个指定大小的线程池。 2. 通过Runnable接口的run方法指定任务内容。 3. 将Runnable任务提交到线程池当中。 4. 可选地关闭线程池(也可以不关闭)
小结一下:
1. 创建:ExecutorService pool = Executors.newFixedThreadPool(2);
2. 定义任务:Runnable task = new ...
3. 提交任务:pool.submit(...)
4. (可选)关闭线程池:pool.shutdown()。(也可以不关闭,重复利用。)
4.线程的分类:
线程分成两种:
用户线程:【主要的】只要没结束,程序就不会退出。
守护线程:【次要的】例如垃圾回收线程,只要所有的用户线程都结束了,那么我也会结束。
线程池默认创建出来的线程全都是用户线程,所以程序不会自动退出的。
如果希望关闭线程池,可以调用shutdown()方法。
5.Shutdow、Fulture关键字
备注:
下面这个Future立刻返回,但是3秒之后里面才能有结果,对于Runnable来说,结果一定是null。
6.异步编程与同步编程
同步编程:就是一种请求响应模型,调用一个方法,等待其响应返回。
这个上面的那个主线程与Pool当中的线程的运行都是在同时进行的。也就是是说重新考虑是否需要响应的地方,也就是缩小需要响应的地方。再者就是将“请求响应”的思路转变到“事件驱动”的思路上,是一种软件编程思维的转变。