原始的写线程方法:
现在的写线程方法:
在使用synolize解决同步问题
Java默认是非公平锁
生产者消费则问题,通知和等待唤醒
生产者与消费者模型中,线程类(比如book)需要写一个生产方法一个消费方法,在生产方法中有if判断,是否count为1,而如为1,表示还没有被消费,需要等待消费,调用wait方法,反之如为0,则表示已经被消费,需要执行逻辑,执行逻辑之后在需要提醒别人进行消费,使用notify方法,消费者的逻辑是类似的。
总结一下,也就是三部曲:判断(是否等待wait)、执行逻辑、唤醒(notity)
wait是会释放锁的,而notifyall会唤醒其他的锁,如果四个线程同时执行的话会并发执行if语句,
要使用while防止虚假唤醒问题
而以上的生产者消费者模型都是在
传统的并发编程,在juc里面,是怎么做的呢?
解读源码可以发现juc的lock机制与synchroize的wait和notify类似的有。
condition,他有condition.await和signal方法支持等待和唤醒
添加的东西是不是简单的notifyall,增加了直接通知某一个线程,
使用多个condition达到精确唤醒的目的
TimeUnit是juc的方法
8锁问题:
并发修改异常,concurrentModificationException
解决方案:vector,还有的修改方案是juc的copyonwirteArraylist
复制并写入;写入时复制读写分离,优点在于,没有synchronize,效率高,
transicent voliate
第一种解决方案是用集合的工具类转为安全的形式;
Collections.synchronizedSet(new HashSet<>());
juc:
同理:copyonwirteSet
HashSet的底层是什么?
就是hashMap,使用key
e就是我们的key,value的这个present是一个常量是不可改变的
key是不可重复的
使用concurrentHashMap
可以有返回值,可以抛出异常
futureTesk继承自Runable,可以将Callable作为构造函数的参数
用来计数的(减法)
示例:教室里面有6个学生,关门的话需要6个人都出去完之后
加法计数器
集齐7颗龙珠召唤神龙
3个停车位6辆车
可用于并发的限流
readlock的实现类:
ReentrantReadWriteLock
多读一写
写入是原子性操作,避免被别人干扰
独占锁就是写锁,共享锁就是读锁
写的时候阻塞的情况(队列满)
读的时候阻塞的情况(队列空)
已经实现的类有ArrayBlockingQueue和LinkBlockingQueue
好处:降低资源消耗(复用)
提高响应速度
方便管理
线程不允许使用excutor创建。
是一个工具类,
线程池用了之后要关闭
函数式接口:有且仅有一个方法,并且有functionInterface注解
由于只有一个方法所以可以用lamda表达式(里面不用写方法名)
JDK8的新特性
consumer消费接口 有参数没有返回值
supplier产生接口,没有参数,有返回值
流式编程
filter(predicate)
map(function)
function第四个应该是
foreach应该是消费者supplier
还体现了链式编程
forkJoin 工作窃取
不让线程去等待,维护的都是双端队列
completableFuturr
使用runasync无返回值
使用supplyasync有返回值
成功了返回啥,失败了返回啥,biconsumer
volatile是虚拟机提供的轻量级的同步机制,而synchronize是重量级的
jmm是java内存模型,是不存在的东西
volatite不能保证原子性(也就是过程不能被其他线程打扰),数据库的四大原则:ACID;
a:atomic原子性
c:coinside 一致性,别人+,自己就会-
i:isolidate隔离性
d:Durability持久性
CAS(compare and set)有点像乐观锁,期望并更新
如果和期望值是相同的话就更新,如果不是的话就异常
在期望的过程中一直循环
缺点:
就是狸猫换太子
左边这个想用最开始的这个A=1,但实际情况是右边这个执行速度快于左边这个,然后右边这个将1修改成3然后又修改为1,这这样造成的结果是在左边用这个A=1的时候已经不是最开始的A了
判断有没有被修改过,这里可以使用乐观锁机制
atomicstampReference(这里的版本号是时间戳)
拿到了外面的锁就可以拿到里面的锁这是自动的