代码改变世界

JAVA JUC 李贺飞

2021-10-14 12:01  cascle  阅读(60)  评论(0编辑  收藏  举报

 

 

 

 

 

 

1.volatile关键字与内存可见性

本质是写的东西没有同步到读缓存

主线程没有更新缓存数据的时间,所以内存里的改动同步不过来

一直读的的缓存里的数据,内存的数据被写了还是读不到

同步锁可以解决,但是效率低

 

volatile:多个线程共享内存数据,保证内存中的数据是可见的。使用内存障碍,保证从主存拿数据

volaile修饰以后,jvm不能重排序了,相较于synchronize轻量级。不需要挂起线程可以一直执行,没有挂起、恢复消耗

但是

  • 没有互斥性
  • 不能保证变量的原子性(不可分割)

 

2.原子变量与CAS算法

本质是写的过程被分割

 

i++ 读改写三步,可能保证内存可见性,但是写内存同步数据,无法互斥,会有重复操作污染数据。i++操作被分割开来,没有原子性了

同时写内存,两条线一样的值同时发生改变成一样的,后写的覆盖了先写的

juc的atomic包

 多个线程同时先从内存读一个数值,当做A,传进去,进行操作的从内存读一下V,一样就设置为更新值B 。读取内存和比较更新是原子操作不可分割,硬件支持

 这就保证了,同时操作,只有一个会成功

 效率比锁高,失败了就重试,不会阻塞流程

 

 

3.模拟CAS算法

 

 

 

3.ConcurrentHashMap锁分段机制

 

 HashTable整个方法执行的收都带锁,具有隐患:复合操作可能被切割,执行一半暂停,回来后状态变了,所以内部加锁没有用,要整个操作加锁。此外并行变串行,效率更低

复合操作本质上是一句话的事情分为两个方法执行,被切割非原子 

 

 ConcurrentHashMap给数据分段加锁,16个段16把锁

 

 

 1.8用CAS替代分段锁

 

全体方法加锁可以使用Collections.synchronizedList

这样在使用迭代器访问并修改集合会有并发修改异常。

 因为迭代next和add操作被切割,写入的时候别人可能在读东西,一写之后再读取就出错(因为可能加在头部,再读迭代器指针就对不上,还读了上一个)。在迭代的过程中,添加和删除元素都会报错,通过modCount这个变量来校验

 使用CopyOnWriteArrayList解决,每次写入时复制新的列表再添加,读的时候读取旧的列表,两不相干。适合读操作多的时候

 

4.CountDownLatch,闭锁

 

 

 

 

 

 

 

 

 

 

 5.创建线程的第三种方式,实现Callable接口

 

 

 

 

 Callable接口给FutureTask,FutureTask给Thread。FutureTask的get可以实现闭锁效果,等待Callable的call方法执行完再获取结果

 Future实现了Runnable接口

 

 7.同步锁

 

 有风险,要在finally里调用,不是百分百被调用

 

 

8.生产者消费者案例:虚假唤醒

 生产者不停地发数据,消费者不停地接数据,没有等待,可能过度生产与消费

如果用if esle而不是if结构,会有以下问题

B缺

A

B

B缺

A

A卡住等待唤醒

本质是B跑的比较快,消费完了马上等待生产但又退出

解决办法是等待、通知后直接进行操作,不要分开,AB或者BA有序

 

wait和notifyAll

所以要加入等待唤醒机制,生产满了等待,生产了通知,消费完了等待,消费了通知

 虚假唤醒:多个等待,一次唤醒后多个源做同一操作,超出了限制。wait要在循环中使用,不要用if用while,这样多一次判断,A、B线程同时等待,A线程执行了B线程可以在条件不满足的时候限制不要顺序执行重新等待。

注意:当线程在某个条件变量下等待时,即使其他线程没有broadcast or signaled 这个条件变量,该线程仍然可能被唤醒,在多核处理器系统下,使条件变量完全可以预测会降低系统的性能,而导致虚假唤醒的几率又很小 

在操作系统底层"唤醒"的实现机制就注定虚假唤醒的存在,设计者们不解决这个问题的原因是

  1. 修复这个问题会导致系统性能下降,性价比太低
  2. 即使修复了这个问题,由于同步问题的存在,仍然要将wait()放进循环里.

 

 8.Condition控制线程通信

 

 

 

 

 10.线程按需交替

三个线程按序执行,每个线程执行前while判断条件,唤醒了满足条件就往下走,唤醒其他条件。这个过程加锁,await会临时解锁

 

 11.ReadWriteLock读写锁

读写分离

乐观锁

同时读不加锁

 

 

 

 

 

 

12.线程八锁

 

 

 13.线程池

 Executor就是线程池

 Executors是工厂工具类,用这个来创建线程池,不要直接创建

 

 

 

 

 

 14.线程池调度

 

 

15.ForkJoinPool分支/合并框架 工作窃取

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

1.读不到写的东西

2.多个写错乱

3.读写并发读错乱