juc

原始的写线程方法:
juc_第1张图片
现在的写线程方法:
juc_第2张图片
juc_第3张图片
在使用synolize解决同步问题

正式篇:

lock(接口)的使用:三部曲

juc_第4张图片

juc_第5张图片

lock的实现类:
在这里插入图片描述
分别是可重入锁,可重入锁的读锁和写锁
在这里插入图片描述

Java默认是非公平锁

现在的问题是lock锁和synchronize的区别在哪;

  • synchronize 是Java内置的关键字,Lock是java类
  • synchronize无法获取到锁的状态,而lock可以
  • synchronize全自动,lock手动
  • 如果有两个线程1,2,线程1获得锁而后面阻塞之后,线程2会一直等待,lock.trylock不一定会傻傻地等待下去。尝试获取等不到就结束
  • synchronize少量同步代码,lock适合大量

锁是什么?如果判断锁的是谁?

生产者消费则问题,通知和等待唤醒
生产者与消费者模型中,线程类(比如book)需要写一个生产方法一个消费方法,在生产方法中有if判断,是否count为1,而如为1,表示还没有被消费,需要等待消费,调用wait方法,反之如为0,则表示已经被消费,需要执行逻辑,执行逻辑之后在需要提醒别人进行消费,使用notify方法,消费者的逻辑是类似的。
总结一下,也就是三部曲:判断(是否等待wait)、执行逻辑、唤醒(notity)
wait是会释放锁的,而notifyall会唤醒其他的锁,如果四个线程同时执行的话会并发执行if语句,
要使用while防止虚假唤醒问题

而以上的生产者消费者模型都是在
传统的并发编程,在juc里面,是怎么做的呢?

解读源码可以发现juc的lock机制与synchroize的wait和notify类似的有。
juc_第6张图片

condition,他有condition.await和signal方法支持等待和唤醒
添加的东西是不是简单的notifyall,增加了直接通知某一个线程,
juc_第7张图片

使用多个condition达到精确唤醒的目的

TimeUnit是juc的方法
8锁问题:

  1. 发短信和打电话,中间用TimeUnit.sleep,先发短信后打电话,是因为锁的对象是方法的调用者,两个对对象都是phone类中的,相当于两个对象用的是统一把锁,
  2. 如果hello没有写上synchronize方法的话,那么就没有同步问题,
  3. 现在是有两个手机,第一个手机发短信,第二个手机打电话,也就是说没有同步,应该是先打电话后发短信,应为是多线程,后面的没有延迟
  4. 增加了两个静态的同步方法,静态是跟着类信息走,所以一直是统一的锁,这时候应该是先发短信后打电话。
  5. 现在是普通的同步方法和静态的同步方法的话,静态跟着类走,普通跟着对象走,只用一个对象进行调用的时候,发短信,打电话,应该是打电话再发短信,因为打电话是普通的同步方法,一个锁的是对象,一个锁的是class,两把锁,两个方法,没有形成竞争关系
  6. 现在是两个对象的话,1发短信,2打电话,发短信还是跟着class走,

集合不安全

list不安全,

并发修改异常,concurrentModificationException
解决方案:vector,还有的修改方案是juc的copyonwirteArraylist
复制并写入;写入时复制读写分离,优点在于,没有synchronize,效率高,
transicent voliate

set不安全

第一种解决方案是用集合的工具类转为安全的形式;
Collections.synchronizedSet(new HashSet<>());
juc:
同理:copyonwirteSet

HashSet的底层是什么?
就是hashMap,使用key
juc_第8张图片

e就是我们的key,value的这个present是一个常量是不可改变的
key是不可重复的

map不安全

在这里插入图片描述
在这里插入图片描述

使用concurrentHashMap

Callable

可以有返回值,可以抛出异常
futureTesk继承自Runable,可以将Callable作为构造函数的参数

juc_第9张图片

juc_第10张图片
结果会被缓冲,拿结果会等待

三大常用的辅助类:

countdownLatch

用来计数的(减法)
示例:教室里面有6个学生,关门的话需要6个人都出去完之后

cyclicBarrir

加法计数器
集齐7颗龙珠召唤神龙

semaphore 信号量

3个停车位6辆车
可用于并发的限流

读写锁

readlock的实现类:
ReentrantReadWriteLock
多读一写

为什么引入读写锁,原因是在多线程条件下,读写发生脏读等情况
juc_第11张图片

写入是原子性操作,避免被别人干扰
独占锁就是写锁,共享锁就是读锁

阻塞队列(blockingQueue)

写的时候阻塞的情况(队列满)
读的时候阻塞的情况(队列空)
已经实现的类有ArrayBlockingQueue和LinkBlockingQueue

四组API

juc_第12张图片
juc_第13张图片

线程池

好处:降低资源消耗(复用)
提高响应速度
方便管理

线程不允许使用excutor创建。

excutor

是一个工具类,

3大方法

在这里插入图片描述
单例的,固定的,还有可伸缩的

juc_第14张图片

线程池用了之后要关闭

7大参数

ThreadPoolExecutor
juc_第15张图片
juc_第16张图片
四种拒绝策略juc_第17张图片

最大线程应该如何设置

juc_第18张图片

四大函数式接口

  1. lamda表达式
  2. 链式编程
  3. 函数式接口
  4. stream流式计算

函数式接口:有且仅有一个方法,并且有functionInterface注解
由于只有一个方法所以可以用lamda表达式(里面不用写方法名)
JDK8的新特性
consumer消费接口 有参数没有返回值
supplier产生接口,没有参数,有返回值

作用:在流式编程中提升编码的效率
juc_第19张图片

流式编程
filter(predicate)
map(function)

function第四个应该是
foreach应该是消费者supplier
还体现了链式编程
juc_第20张图片
forkJoin 工作窃取
不让线程去等待,维护的都是双端队列

异步回调

completableFuturr
使用runasync无返回值
使用supplyasync有返回值

成功了返回啥,失败了返回啥,biconsumer

juc_第21张图片
T是正确消息,U是错误的消息

JMM

volatile是虚拟机提供的轻量级的同步机制,而synchronize是重量级的

  1. 保证可见性
  2. 不保证原子性
  3. 禁止指令重排

jmm是java内存模型,是不存在的东西

volatite不能保证原子性(也就是过程不能被其他线程打扰),数据库的四大原则:ACID;
a:atomic原子性
c:coinside 一致性,别人+,自己就会-
i:isolidate隔离性
d:Durability持久性

什么是指令重排

CAS

CAS(compare and set)有点像乐观锁,期望并更新

如果和期望值是相同的话就更新,如果不是的话就异常

自旋锁

在期望的过程中一直循环

缺点:

  1. 由于底层是自旋锁,耗时,
  2. 一次只能保证一个共享变量的原子性
  3. ABA问题

ABA问题

就是狸猫换太子
juc_第22张图片
左边这个想用最开始的这个A=1,但实际情况是右边这个执行速度快于左边这个,然后右边这个将1修改成3然后又修改为1,这这样造成的结果是在左边用这个A=1的时候已经不是最开始的A了

判断有没有被修改过,这里可以使用乐观锁机制

如何解决ABA问题

带版本号的原子引用(和乐观锁类似)

atomicstampReference(这里的版本号是时间戳)

可重入锁

拿到了外面的锁就可以拿到里面的锁这是自动的

你可能感兴趣的:(juc)