多线程-20200422-高级应用
消息队列:生产消费者模型
【1】生产者
【2】消费者
【3】队列
-生产者生产数据,消费者依次消费
-生产者当队列满,不再生产
-消费者当队列空,进入等待
【4】消息中间件:本质同理。
note:this.wait()\this.notifyAll()——获取锁。如果是同一个对象,会直接往下执行。----所以需要多次判断。放弃if , while()
解决问题的思路——技术【池】
【1】SE定制化高 && 实际生产往往更注重效率。
【2】将一些对象放入“池”当中,然后交给池托管这些对象的执行方式。作为使用者,不需要再关心如何管理这些对象
【3】线程池管理分类:
固定线程池:线程池中,线程
【4】单任务线程,类似join,线程串行执行
-所有线程用完归池
-依次执行
【5】可变尺寸的线程池,全部并发
-
【6】延迟线程池
-延迟的线程是在其被加入到池当中以后,开始计时。
-当前线程资源,延迟线程【顺延】
-延迟线程一样需要竞争资源
【7】自定义线程池——队列
-至少等于
-核心线程数量规定当前
-核心线程数满了后,加入队列;队列是同时可以执行的线程数
-队列线程数满了后,如果最大线程池的数量没满,则队列外多余的线程同时执行。但:这些线程是不经过线程池管理的线程,他们没有做到对象的重用。
-如果队列满了,最大线程池数量也满了,则不再接受新的线程执行
-队列内的线程会被核心线程重复取用
【8】有返回值的线程,实现callable接口
-任何线程必须join到Callable之后,才会执行。
-应用场景:当当前线程需要前一个线程的执行结果,才能继续执行时,需要get()结果。
-如果不需要返回值,则其和普通线程没有任何区别。
自定义线程池
【1】同步方法、同步代码块
【2】使用Lock接口:可以使线程的操作更加精细化
【3】IO流阻塞和运算阻塞【设计线程,设计同步】
-IO流阻塞:对于数据进行读写或者其他IO操作【偏移量】。——建议尽可能多的划分线程。
-运算阻塞:在数据的运算过程中,CPU是不会释放资源的。——这种情况下,我们线程的划分最大数量就是CPU的内核数量。
-大数据当中的流式计算:将运算阻塞转化为IO流阻塞。
【4】读写锁——某一个对象的所有线程,本质锁的是: ReentrantReadWriteLock 对象
-当多个线程同时访问读锁时,不受影响,可以同时访问。
-当某个线程获得写锁时,其他线程既不能读,也不能写。【写锁属于独占锁,排他锁】
-如果先执行读锁,直到所有读锁执行结束,开始写锁。
-锁只有降级,没有升级;因为写锁时独占锁,而读锁时非独占锁。
-公平锁
1:获取锁的模式包括:公平锁和非公平锁。
2:公平锁:on:true。不存在饥饿状态的线程,所有线程有获得公平的执行几率。【效率会较低】
3:非公平锁:(默认)on:false。在某种极端情况下,会出现某个或者某几个线程始终处于饥饿状态。
4:重进入:某个线程,同一时刻,可以持有多个锁。
5:降级性:写锁的强度要高于读锁。遵循:获取写锁、读锁,释放写锁,此时成为:写锁降级为读锁。
con:
1:先获取读锁的情况下,再获取写锁,此时获取写锁失败。【注意:只要发现当前的读锁被占用,则无法获取写锁,不管读锁是否被当前线程持有】
2:线程持有写锁情况下,可以继续获取读锁。【注意:获取读锁时,发现写锁被占用,当且仅当写锁没有被当前线程占用的情况下失败】——获取写锁的同时,往往会独占读锁。
3:由于读锁允许多个线程同时占有,因此读锁不可以升级为写锁。——而写锁往往是独占读锁,可以降级。
【5】信号量
-未达到上限时,可以正常执行;超过上限,则进入阻塞状态。
【6】线程池——阻塞队列 && 阻塞栈
-队列是一个单向通道;超出上限以后进入阻塞;没有元素进入阻塞
-栈是一个双向通道;超出上限以后,返回false;没有元素返回null
【7】条件变量
-持锁状态下,可以等待和唤醒
-支持多个对象唤醒
【8】原子量 && ThreadLocal
-即便是原子量,但是整个流程可能亦然不安全,需要线程加锁。
-保障多个线程对同一个数据的可见性和线程安全性
[9]障碍器
-有若干子任务需要执行,某个线程需要等到所有的子任务执行完成,才能执行主任务。——障碍器
-
线程中:不允许修改,建议用final;同理传参也是final
构造:传参必须:static final 如果为不可变的话!
final 变量问题
总结一下:不同应用场景
ArrayList &&& linkedList