概念扫盲:
ReentrantLock: 独占锁/排他锁
CountDownLatch 计数器 递减
CyclicBarrier 计数器 自增
SemaPhore 信号量
AQS=AbstractQueuedSynchronizer类, 即抽象的同步队列==》解决数据安全问题(并行转换为串行)。
造成数据安全问题的原因是什么?多线程,并发。
假设多个线程对count进行处理:
假设我们启动100个线程对count进行自增操作:
正常执行后:
得到的结果是一个小于等于1000的随机值。并不是1000.
这个问题如何解决?造成数据安全问题的原因是什么?
对应的解决方案是什么?
1.可见性,即JVM内存模型: Java线程中有一块工作内存,与主内存进行交换。
通过volatile,synchronized Lock可以进行处理。
2.有序性:
指令重排序:可能指令顺序会被调整,通过volatile,synchronized,Lock可以解决。
3.原子性:
原子是最小的单位,不可分割。 指一段Java代码要么都执行,要么都不执行。
比如 int a=10;
Int b=a=100;
Int c=a+b;
假如有三个线程访问,其中一个线程执行a=0,需要等他执行完后面两句后,其他线程才能处理。
Synchronized, Lock, AtomicInteger可以解决原子性问题。
这里面就有一个原子性问题:
今天采用Lock来解决,在代码中增加lock和unlock加锁和减锁操作:
执行结果为1000:
为什么增加lock和unlock后可以解决问题呢?
多个线程过来后,lock将原来并行的操作转换为了串行的操作。只有一个线程操作数据。
加入共享的数据是关键数据,显然需要加lock进行处理,保证数据安全。
Lock有哪些功能来保证锁的功能呢?
1.排他性,独占性
Int state=0;//约定state=0表示锁空闲,state>0标识锁被占用
If(state==0){
//锁空间
State++;
}else{
//表示锁被占用
Wait(); //阻塞
}
2. 存储没有抢占到锁的对象
New Thread();数组,集合,链表,队列
3.阻塞没有抢占到锁的线程
Wait();
LockSupport.park();
Unlock应该有哪些功能呢?
1.释放锁
State--
2.唤醒阻塞的线程
Notify/notifyAll
LockSupport.unpark();
到此,我们可以去验证JDK中,lock和unlock是如何实现排他性的,如何实现阻塞和唤醒的,可以查看源码。
那Lock和AQS是什么关系呢?AQS的作业是什么? 能够帮助我们解决什么问题?
Lock中的FairSync(公平锁)和NonfairSync(非公平锁)相关方法等都是继承自AQS。
Lock等功能都是继承自AQS的sync.lock()等实现的。
在第一次加锁后,在第二个count--的方法再次加锁,这就是重入锁,并不会造成死锁。
公平锁是按照顺序执行,而非公平锁则是插队。
Lock的具体调用源码:
NonfairLock(非公平锁)的核心代码如下,获取锁后抢占expect:0, 设置state为1:
如果没抢占到,acquire方法处理,
之后可以找到具体nonfairTryAcquire方法代码:
要么获取锁,要么重入锁,否则返回false。
创建了一个非公平锁的Node, head和tail默认为空。
代码逻辑如下:
所以,所有没有抢占到的对象,都被串联到链表中:
阻塞锁的代码实现:
如果判断是否为头结点,是则获取锁。
分布式锁的应用场景分析:
论学习的连贯性:
我们经常花了很多时间,学了很多东西,但是过了一段时间又忘了,感觉好难,心累!
学习的东西是孤立的,需要坚持下来,这些孤立的是能够串联起来的。
任何一个知识都有它的作用,应该按照路线来学习。
- 》B—》C—》D(工作中的技术)
比如: 微服务—》Springboot—》Spring—》Mybatis—》设计模式+Java基础进阶
我们按照反向的进度,逐一进行理解。
比如装饰器模式在ibatis中:
这样,知识点串联起来后就不容易忘记了。
又比如:
计算机网络+操作系统—》BIO-》NIO+线程池+多路复用—》Netty—》RPC—》ZK Redis Ribbon Dubbo……
Redis LUR 操作系统 线程……
Ribbon—》SpringBoot+代理模式+IOC+定时调度+负载算法+RPC通信……