JUC 第一天:lock锁与多线程
1. 是什么?
java.util.concurrent 包的简称! 这里面有很多并发编程时需要使用的工具类!
2. 多线程相关内容回顾:面试题! *****
线程:
通常一个进程中包含若干个线程, 分配资源的基本单位!
进程:
具有一定独立功能的程序!
并发:
在同一时刻访问同一个资源。
并行:
多个工作一起执行,之后汇总到一起。是一个整体!
打扫卫生!
wait:
释放资源锁!
Object 类!
sleep:
不释放资源锁!
Thread 类!
3. 如何创建线程:
1. 继承:
Thread
2. 实现:
Runnable
Callable 接口!后续处理!
// 匿名内部类创建线程!
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("我是匿名的....");
}
}).start();
// 学过拉姆达表达式!
// 回顾拉姆达表达式
()->{}
箭头右边:方法体!
箭头左边:方法的参数!
public abstract void run();
new Thread(()->{System.out.println("我是拉姆达表达式");}).start();
// 公式:复制小括号,写死右箭头,落地花括号!
案例:
当接口中只有一个抽象方法时,这样的接口叫函数式接口! 才可以使用拉姆达表达式去写!
@FunctionalInterface 这个注解的作用就是: 如果这个接口中出现了多个抽象方法,会给你提示错误信息!
interface Foo {
public int add(int x, int y);
}
函数式接口中是否可以存在其他方法?
@FunctionalInterface
interface Foo{
// 定义一个抽象方法!
int add(int x,int y);
// 除法!
// int div(int x,int y);
// 这个是默认方法:
default int div(int x,int y){
return x/y;
}
// 还可以有静态方法:
// static 特性: a. 随着类的加载而加载 b. 优先于对象的存在 c. 类名.方法();
static int decr(int x,int y){
return x-y;
}
}
必会:*****
当接口中只有一个抽象方法时,这样的接口叫函数式接口! 才可以使用拉姆达表达式去写!
@FunctionalInterface 这个注解的作用就是: 如果这个接口中出现了多个抽象方法,会给你提示错误信息!
公式:复制小括号,写死右箭头,落地花括号!
4. sync 回顾:
这个关键字什么时候会用?
多线程出现资源抢占的时候!
回顾卖票的案例!
多线程编程模板上: 线程操作资源类!
重点: synchronized 八种锁问题!
总结:*****
1. 两个同步方法执行的时候,这个两个方法的使用的应该是同一把锁! 这个锁是谁呢? 当前对象this!
2. 如果是一个同步方法,一个是普通方法。则同步方法有锁,普通方法没有锁!按照顺序执行!
3. 如果是两部手机调用的同步方法,他们不是同一把锁!按照顺序执行!
Phone phone = new Phone();
Phone phone2 = new Phone();
4. 静态同步方法:锁字节码文件!普通同步方法:锁 this!
对于同步方法块,锁是Synchonized括号里配置的对象!
5. Lock 接口!
lock() 上锁!
tryLock()
tryLock(long time, TimeUnit unit)
newCondition() 获取钥匙
unlock() 解锁!
a. ReentrantLock 先看构造:
ReentrantLock(); 非公平锁!
ReentrantLock(boolean fair); false 非公平锁! true 公平锁!
b. 使用!*****
可以代替sync!
lock.lock();
try{
// 业务逻辑.....
}finally{
lock.unlock();
}
5.1 可重入锁: *****
同一个线程,在外层获取到锁的时候,在进入线程的内部同步方法时,会自动获取到锁!这就叫可重入锁!
sync, ReentrantLock 都属于可重入锁!
在一定程度上可以避免死锁的产生!
5.2 测试公平锁 限时等待:
private Lock lock = new ReentrantLock(true);
注意点:
有加锁,就必须有解锁!
5.3 ReentrantLock和synchronized区别! *****
a. synchronized and ReentrantLock 独占锁,可重入! 加锁,解锁不受程序员控制器,都是有JVM 操作!ReentrantLock 反之!操作灵活!
b. synchronized不可响应中断,一个线程获取不到锁就一直等着;ReentrantLock可以响应中断!
5.4 ReentrantReadWriteLock 读写锁!
构造:
ReentrantReadWriteLock()
ReentrantReadWriteLock(boolean fair)
读写锁:
允许同一时刻被多个读线程访问数据,但是,在写线程中,只有允许一个线程!其他都会阻塞!
案例:
读方法,写方法!
有问题案例:
优化案例:
特点: *****
写写,读写,写读 不能并发操作!
读读 可并发!
锁的降级:
从写锁变为读锁 就是锁的降级!释放写锁,再释放读锁!
读写锁总结 *****
优点:
支持,公平、非公平锁!
可重入!
锁的降级,不支持锁升级!
缺点:
容易产生饥饿问题!
读多,写少的情况下! 可以使用公平模式解决!以牺牲系统的吞吐量为代价!
写锁可以获取到Condition 对象,但是,读锁不可以获取,会抛出异常!
6. 线程间的通信:
多线程编程模板上:线程操作资源类!
多线程编程模板中:判断,干活,通知!
两个线程操作一个初始值为0的变量,实现一个线程对变量增加1,一个线程对变量减少1,交替10轮!
两个是没有问题的! 都是1,0 !
如果出现一对以上的线程就不是1,0了!
原因:*****
虚假唤醒!
if---->while
多线程编程模板下: 防止虚假唤醒 判断使用while!
线程间通信:对标! *****
sync ---> lock
Object Condition
wait(): await();
notify(): signal();
notifyAll(): signalAll();
JUC 第二天:
回顾:
1. 回顾有关于多线程的面试题:
线程,进程:
并发,并行:
wait与sleep:
2. 有关于锁 sync 回顾!
sync ----> lock
Object Condition
wait(): await();
notify(): signal();
notifyAll(): signalAll();
3. LOCK 接口:
ReentrantLock和synchronized区别!
lock.lokc();
try{
// 业务逻辑!
}finally{
lock.unlock();
}
限时等待方法:lock.tryLock(param1,param2);
可重入锁,独占锁
公平与非公平锁!
sync a(){
b();
}
sync b(){};
读写锁:
特点:
读读可并发,读写,写读,写写都不可以并发!
从写锁到读锁这个过程,解锁写锁读锁,这个过程称之为锁的降级!
读写锁最大的缺点:很容易会产生'饥饿'状态!
在读线程多,写线程少的情况下! 使用公平与非公平机制来调节,以牺牲系统的吞吐量为代价!
4. 线程间通信:
Object Condition
wait(): await();
notify(): signal();
notifyAll(): signalAll();
多线程编程模板:
上:线程操作资源类,
中:判断干活通知,
下:防止虚假唤醒! 在使用多线程判断的时候 if ---> while
1. 线程间定制化通信!
AA打印5次,BB打印10次,CC打印15次
接着
AA打印5次,BB打印10次,CC打印15次
。。。打印10轮
分析:
1. Lock 有锁!
2. condition 钥匙!
3. flag 标识! flag = 1 AA 2 BB 3 CC
4. flag 的值修改!
2. 并发容器类!
List 集合!
在多线程的时候,向集合容器中添加数据,则会报错!
java.util.ConcurrentModificationException:并发修改异常!
底层代码:
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
集合添加add();方法的底层源码!
public boolean add(E e) {
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}
解决方案:
1. 使用Vector 集合,线程安全的! add 方法有锁!
public synchronized boolean add(E e) {
modCount++;
ensureCapacityHelper(elementCount + 1);
elementData[elementCount++] = e;
return true;
}
2. 使用集合工具类!
List<String> list = Collections.synchronizedList(new ArrayList<>());
vector:**内存消耗比较大**,适合一次增量比较大的情况 ,同步方法!
SynchronizedList:**迭代器涉及的代码没有加上线程同步代码** 非迭代器部分,使用是同步代码块!
3. CopyOnWriteArrayList 底层原理:写时复制技术,思想,读写分离!
add();
底层源码:
public boolean add(E e) {
final ReentrantLock lock = this.lock;
lock.lock();
try {
Object[] elements = getArray();
int len = elements.length;
Object[] newElements = Arrays.copyOf(elements, len + 1);
newElements[len] = e;
setArray(newElements);
return true;
} finally {
lock.unlock();
}
}
使用场景:多读写少!
CopyOnWriteArrayList 获取数据的时候有锁么?写入数据的时候,需要创建一个副本!
缺点:
1. 需要足够大的内存!
2. 可能会出现数据不一致的问题! 保证的数据最终一致性!
在多线程操作的情况下:ArrayList 推荐使用 CopyOnWriteArrayList
Set 集合 ,Map 集合!
Set 集合不安全:
java.util.ConcurrentModificationException
解决方案:CopyOnWriteArraySet 类!
源码:
private final CopyOnWriteArrayList<E> al;
public boolean add(E e) {
return al.addIfAbsent(e);
}
public boolean addIfAbsent(E e) {
Object[] snapshot = getArray();
return indexOf(e, snapshot, 0, snapshot.length) >= 0 ? false :
addIfAbsent(e, snapshot);
}
// 写时复制技术!
private boolean addIfAbsent(E e, Object[] snapshot) {
final ReentrantLock lock = this.lock;
lock.lock();
try {
Object[] current = getArray();
int len = current.length;
if (snapshot != current) {
// Optimize for lost race to another addXXX operation
int common = Math.min(snapshot.length, len);
for (int i = 0; i < common; i++)
if (current[i] != snapshot[i] && eq(e, current[i]))
return false;
if (indexOf(e, current, common, len) >= 0)
return false;
}
Object[] newElements = Arrays.copyOf(current, len + 1);
newElements[len] = e;
setArray(newElements);
return true;
} finally {
lock.unlock();
}
}
Map: 集合!
java.util.ConcurrentModificationException
解决方案:
ConcurrentHashMap!
源码:
public V put(K key, V value) {
return putVal(key, value, false);
}
重点:***** 同步代码块!
final V putVal(K key, V value, boolean onlyIfAbsent) {
if (key == null || value == null) throw new NullPointerException();
int hash = spread(key.hashCode());
int binCount = 0;
for (Node<K,V>[] tab = table;;) {
Node<K,V> f; int n, i, fh;
if (tab == null || (n = tab.length) == 0)
tab = initTable();
else if ((f = tabAt(tab, i = (n - 1) & hash)) == null) {
if (casTabAt(tab, i, null,
new Node<K,V>(hash, key, value, null)))
break; // no lock when adding to empty bin
}
else if ((fh = f.hash) == MOVED)
tab = helpTransfer(tab, f);
else {
V oldVal = null;
synchronized (f) {
if (tabAt(tab, i) == f) {
if (fh >= 0) {
binCount = 1;
for (Node<K,V> e = f;; ++binCount) {
K ek;
if (e.hash == hash &&
((ek = e.key) == key ||
(ek != null && key.equals(ek)))) {
oldVal = e.val;
if (!onlyIfAbsent)
e.val = value;
break;
}
Node<K,V> pred = e;
if ((e = e.next) == null) {
pred.next = new Node<K,V>(hash, key,
value, null);
break;
}
}
}
else if (f instanceof TreeBin) {
Node<K,V> p;
binCount = 2;
if ((p = ((TreeBin<K,V>)f).putTreeVal(hash, key,
value)) != null) {
oldVal = p.val;
if (!onlyIfAbsent)
p.val = value;
}
}
}
}
if (binCount != 0) {
if (binCount >= TREEIFY_THRESHOLD)
treeifyBin(tab, i);
if (oldVal != null)
return oldVal;
break;
}
}
}
addCount(1L, binCount);
return null;
}
HashSet底层数据结构是什么?HashMap ?
set.add(value) value 是 HashMap 的key,还是value?
key!
源码:
private transient HashMap<E,Object> map;
private static final Object PRESENT = new Object();
public boolean add(E e) {
return map.put(e, PRESENT)==null;
}
3. JUC强大的辅助类:
a. CountDownLatch(倒计数器)
构造:
CountDownLatch(int count) // 初始化count 个数! 6;
方法:
await() :阻塞线程用的!
countDown() :减少计数
案例:
6个同学陆续离开教室后值班同学才可以关门!
面试题:
CountDownLatch 与 join 方法的区别?
CountDownLatch:在运行的时候,可以调用await()方法! 调用了这个方法之后,CountDownLatch 并不一定会结束!
Join:调用一个子线程的Join(); 这个线程会被阻塞,直到子线程执行完毕!
b. CyclicBarrier(循环栅栏)
构造:
CyclicBarrier(int parties) 初始化值 屏障个数!
CyclicBarrier(int parties, Runnable barrierAction) 初始值,线程!
当达到一个屏障点的时候,触发一个线程操作!
方法:
await() :阻塞线程用的!
案例:组队打boss过关卡游戏!
面试题:
CyclicBarrier和CountDownLatch的区别?
CountDownLatch的计数器只能使用一次, 减少计数到达目的!
CyclicBarrier的计数器可以使用reset()方法重置,调用多次, 达到这个屏障才能触发线程!
c. Semaphore(信号量)
构造:
Semaphore(int permits) :初始化值 指的是资源个数!
Semaphore(int permits, boolean fair) 初始化值,公平、非公平!
方法:
acquire() :获取到资源!
release() :释放资源!
案例:6辆车抢占3个车位!
4. Callable接口
Callable接口,在实现线程的有异常信息,同时还有返回值! 能够创建线程!
面试题: Callable和Runable对比!
Callable:实现call 方法,有返回值,有异常! isDone(), get();
Runnable: 实现run 方法,没有返回值,没有异常!
如何使用Callable接口!
创建线程要找中间人!
FutureTask!
前端的小妹妹,找一个合适的!
找班主任导师.....
拉跟线.....
线程类构造:
Thread(Runnable target);
Thread(Runnable target, String name);
根据线程类的构造:我们想办法让Callable 接口 与Runnable 接口有关系!
Runnable 这个接口中有个 实现类 FutureTask!
构造:
FutureTask(Callable<V> callable);
// Runnable 这个接口中有个 实现类 FutureTask!
// FutureTask(Callable<V> callable);
FutureTask futureTask = new FutureTask<>(new MyThread2());
// Thread(Runnable target);
new Thread(futureTask).start();
System.out.println(futureTask.get());
get(); 获取到返回值结果!通常都放在最后执行!并且它只执行一次!
isDone(); 表示判断当前任务是否执行完成!
FutureTask:通常表示执行一个相对比较耗时,或者说未来任务!
面试题:
创建线程四种方式:
实现Runnable 没有返回值.
实现Callable 又返回值.
继承Thread 少用!Java 单继承! 慎用!
线程池 提高项目运行速度!
5. 阻塞队列:
回顾:
栈:先进后出,后进先出
刷盘子!
队列:先进先出
排队做核酸!
阻塞队列:
在多线程领域中,能够自动识别”任务“什么时候阻塞,什么唤醒! 多线程执行任务的时候,如果队列满了,则有新任务过来的时候,
它会自动阻塞,如果有线程处理完成一个任务之后,这个阻塞队列中的任务,则会被处理,后续的任务就能够进来了!
特点: 必会!
1. 有任务进来的时候,当队列满了进行排队{阻塞}!
2. 当队列中为空的时候,线程获取任务时,则会发生阻塞!
为什么需要BlockingQueue?
1. 高效!
阻塞队列分类:了解!
ArrayBlockingQueue: 由数组结构组成的有界阻塞队列。
LinkedBlockingQueue:由链表结构组成的有界(但大小默认值为integer.MAX_VALUE)阻塞队列。
SynchronousQueue:不存储元素的阻塞队列,也即单个元素的队列。
了解阻塞队列方法:
put:
take:
阻塞队列使用场景:
线程池中!
JUC 第三天:
回顾:
1. 并发容器类:必会!
ArrayList:CopyOnWriteArrayList
HashSet:CopyOnWriteArraySet {CopyOnWriteArrayList}
底层原理都是”写时复制”技术!
HashMap:ConcurrentHashMap
使用sync
HashSet 添加数据的时候,add(value); 底层HashMap ,那么value 是作为 HashMap 的 key,来存储的!
请你说一下对集合的认识:
从集合的框架图:
List , Set 区别? Set 无序不重复? set.add(new Stu(1,"张三")); set.add(new Stu(2,"李四"));
2. HUC 强大辅助类:了解
1. CountDownLatch(倒计数器)
2. CyclicBarrier(循环栅栏)
3. Semaphore(信号量)
3. Callable 接口! 必会! 函数式接口!
创建线程的方式! 4种!
Callable 与 Runnable 区别?
中间人:
FutureTask:用来执行未来任务!
// 执行线程!
new Thread(new FutureTask(new Callable)).start();
4. 阻塞队列:
特点:
1. 当队列中满了,再向队列中添加数据的时候,则会发生阻塞! put();
2. 当队列为空的时候,从队列中获取数据,则会发生阻塞!take();
使用场景:线程池!
重点:
1. 线程池!
2. 多线程并发底层原理!
1. 线程池
优势:线程复用,控制最大并发数,管理线程!
需要记住的类:Executor,ExecutorService,ThreadPoolExecutor, Executors 这几个类。
案例:如何创建线程池!Executors!
a. Executors.newCachedThreadPool(); 表示创建一个可扩容的线程池!
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
b. Executors.newSingleThreadExecutor(); 创建一个线程池,但是这个池中就一个线程! 一池一线程!
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
c. Executors.newFixedThreadPool(3); 指定线程池中有几个线程!
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
最终代码:都会指向 ThreadPoolExecutor 这个类!
线程池7个核心参数: *****
【ThreadPoolExecutor】
public ThreadPoolExecutor(
int corePoolSize, //核心线程数
int maximumPoolSize, //最大线程数,这个值必须要大于等于1
long keepAliveTime, //空闲线程存活时间
TimeUnit unit, //空闲线程存活时间单位
BlockingQueue<Runnable> workQueue, //阻塞队列
ThreadFactory threadFactory, //线程池工厂 ,通常都是默认!Executors.defaultThreadFactory()
RejectedExecutionHandler handler //拒绝策略 ,通常默认!new AbortPolicy(); 工作线程大于最大线程数的时候
){
// 业务逻辑:
}
第六,七个个参数,可以不用给值!可以省略..... 都有自己的默认值!
核心线程个数的设置?
n:CPU 的核数!
IO密集型:
2*n
CPU密集型:
n+1
线程池底层工作原理: *****
见图:
自定义线程池案例:
以后面试的时候问你创建线程池使用 哪种? *****
Executors.newCachedThreadPool();
Executors.newSingleThreadExecutor();
Executors.newFixedThreadPool(3);
底层都是使用的ThreadPoolExecutor!
答案都不用! 用了,可能会导致OOM! 创建线程的最大个数是 Integer 最大值,或者是队列没有限制无限存储!
需要根据业务自定义线程池!
用的都是自定义线程池!
如何判定这个线程池能够执行最多任务是多少?
最大线程个数+阻塞队列个数!
拒绝策略:RejectedExecutionHandler!
AbortPolicy(默认) 抛出异常!
CallerRunsPolicy: 没有异常,不会抛弃任务,让调用者去执行任务!
DiscardOldestPolicy:抛弃队列中等待最久的任务,不会抛出异常!
DiscardPolicy:该策略默默地丢弃无法处理的任务
自定义拒绝策略!
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
2, //核心线程个数
5, //最大线程个数
3L, //空闲线程存活时间
TimeUnit.SECONDS, //空闲线程存活时间单位
new ArrayBlockingQueue<>(3), //阻塞队列
Executors.defaultThreadFactory(), //默认线程池工厂
// new ThreadPoolExecutor.DiscardPolicy()// 默认的拒绝策略!
new RejectedExecutionHandler() {
@Override
public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
System.out.println("别来人了.....");
}
}
);
2. 多线程并发底层原理
a. 计算机运行:CPU ---> CPU 缓存 ----> 主内存!
b. Java内存模型{java memory model}
目的:使用同样的代码能够在不同的环境中运行出同样的效果!
本质上是没有关系的!
JMM: 主内存和工作内存
JVM: 堆,栈,方法区
工作内存:栈!
主内存:堆中对象实例部分!方法区
主内存对应的是硬件的物理内存,工作内存对应的是寄存器和高速缓存!
JMM 对主内存规定:*****
- 线程对共享内存的所有操作都必须在自己的工作内存中进行,不能直接从主内存中读写;
- 不同线程无法直接访问其他线程工作内存中的变量,因此共享变量的值传递需要通过主内存完成。
JMM 三大特性:*****
1. 原子性:
Integer a = 0; 不能分割的,原子性操作!
a++ 是可以分割的,a=a+1; 非原子性操作!多线程中存在安全隐患!解决方案使用锁,或者使用院子类解决!
2. 可见性:
当A 线程修改了主内存中的数据时,可以让B 线程可见!
在 Java 中 volatile、synchronized 和 final 实现可见性
3. 有序性:
多线程中,让线程读取顺序是有序的。为什么会出现无序的状况:{因为主内存与工作内存在传进的时候存在一定的延迟}
volatile:
synchronized:
c. volatile关键字 可见性:
// 可见性:
public class VolatileDemo {
// 声明一个变量:
private static volatile Integer flag = 1;
public static void main(String[] args) throws InterruptedException {
// 创建一个线程
new Thread(()->{
System.out.println("我是子线程:flag 的值:"+flag);
// 循环 如果子线程感知到flag = 2 就会结束,说明可见! 否则就不会结束!说明不可见!
while (flag==1){
}
System.out.println("子线程结束......");
}).start();
// 睡眠:
Thread.sleep(400);
flag=2;
System.out.println("主线程:flag 的值:"+flag);
}
}
后续代码省略....
总结:volatile关键字,可见性 有序性,不具备原子性:
1. 被volatile修改的变量,认为它是共享变量,可以被其他线程感知!比sync更轻量级的同步机制!
2. 禁止重排!
3. 不保证原子性!
4. 读性能可以,但是写的性能稍差!
了解 Happen-Before 原则:
概念:当A操作先行发生于B操作,则在发生B操作的时候,操作A产生的影响能被B观察到
了解:Happen-Before的规则
CAS:
概念:Compare and Swap。比较并交换的意思。CAS操作有3个基本参数:内存地址A,旧值B,新值C
if(内存地址A.equals(旧值B)){
新值C
}else{
不能更新!
}
作用:CAS是解决多线程并发安全问题的一种乐观锁算法!
// 对象、对象的属性地址偏移量{参数}、预期值、修改值
public final native boolean compareAndSwapInt(Object var1, long var2, int var4, int var5);
案例:
AtomicInteger:具有原子性的操作类!
优点:AtomicInteger 原子性操作!在多线程情况下能够保证数据安全!
缺点:
1. 开销大!
2. ABA问题!
3. 不能保证代码块具有原子性,只能保证的一个变量具有原子性!
AQS:
读写锁最大的缺点:很容易会产生'饥饿'状态! 读写锁的升级 :"StampLock" 能够解决锁的饥饿问题!扩展!
通过AQS:可以实现独占锁!
ReentrantLock 底层实现原理 就是 AQS
非公平锁时lock 方法:
// 锁方法
private Lock lock = new ReentrantLock();
lock.lock();
// Sync 类下有个属性!
abstract void lock();
// 全局资源变量 默认值 0
private volatile int state;
// 属于:NonfairSync
final void lock() {
if (compareAndSetState(0, 1)) // CAS把 state 设置为 1
setExclusiveOwnerThread(Thread.currentThread()); // 当前拥有锁的线程 获取到了锁!
else
acquire(1); // 获取锁失败!
}
// 赋值当前拥有锁的线程方法!
protected final void setExclusiveOwnerThread(Thread thread) {
exclusiveOwnerThread = thread;
}
public final void acquire(int arg) {
if (!tryAcquire(arg) && // 表示再次尝试获取锁失败,
acquireQueued(addWaiter(Node.EXCLUSIVE), arg)) // 排队!
selfInterrupt();
}
protected final boolean tryAcquire(int acquires) {
return nonfairTryAcquire(acquires);
}
// acquires = 1
final boolean nonfairTryAcquire(int acquires) {
final Thread current = Thread.currentThread(); // 获取到当前线程
int c = getState(); // 初始化是0,
if (c == 0) {
if (compareAndSetState(0, acquires)) { // CAS 比较交换!获取到锁!
setExclusiveOwnerThread(current); // 获取锁成功,设置当前线程为获取到锁的线程!
return true;
}
}
// 如果获取到的c 不是0,则说明已经有锁了!
else if (current == getExclusiveOwnerThread()) { // 可重入体现!拥有锁的线程与当前线程是一个线程!
int nextc = c + acquires;
if (nextc < 0) // overflow
throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
return false;
}
// 这个方法是获取到当前拥有锁的线程是谁!
protected final Thread getExclusiveOwnerThread() {
return exclusiveOwnerThread;
}
// 解锁:
lock.unlock();
public void unlock() {
sync.release(1);
}
// 释放资源:
public final boolean release(int arg) {
if (tryRelease(arg)) {
Node h = head;
if (h != null && h.waitStatus != 0)
unparkSuccessor(h);
return true;
}
return false;
}
// unlock 一次 从 state 中 -1 操作!
protected final boolean tryRelease(int releases) {
// 可重入锁 state = 2; unlock 一次! state = 1 再次调用unlock ,则state = 0;
int c = getState() - releases;
if (Thread.currentThread() != getExclusiveOwnerThread()) // 解锁的时候,必须是加锁的线程解锁!
throw new IllegalMonitorStateException();
boolean free = false;
if (c == 0) {
free = true;
setExclusiveOwnerThread(null); // 需要将拥有锁的线程的变量变为空!
}
setState(c);
return free;
}
公平锁:
FairSync 类!
final void lock() {
acquire(1);
}
public final void acquire(int arg) {
if (!tryAcquire(arg) && // 表示获取到锁!
acquireQueued(addWaiter(Node.EXCLUSIVE), arg)) // 如果没有获取到就排队
selfInterrupt();
}
protected boolean tryAcquire(int arg) {
throw new UnsupportedOperationException();
}
protected final boolean tryAcquire(int acquires) {
final Thread current = Thread.currentThread(); // 获取当前线程!
int c = getState(); // 获取state 状态!
if (c == 0) {
if (!hasQueuedPredecessors() && // 要做排队了!
compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);
return true;
}
}
else if (current == getExclusiveOwnerThread()) {
int nextc = c + acquires;
if (nextc < 0)
throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
return false;
}
// 公平锁的核心代码! 按照顺序进行排队!
public final boolean hasQueuedPredecessors() {
// The correctness of this depends on head being initialized
// before tail and on head.next being accurate if the current
// thread is first in queue.
Node t = tail; // Read fields in reverse initialization order
Node h = head;
Node s;
return h != t &&
((s = h.next) == null || s.thread != Thread.currentThread());
}
static final class Node {
...
}
// AbstractQueuedSynchronizer 类!有个Node 类,
volatile Node next;