共计 4353 个字符,预计需要花费 11 分钟才能阅读完成。
1.AQS 是什么?
AQS(AbstractQueuedSynchronizer)形象队列同步器, 是 JDK 下提供的一套实现基于 FIFO 期待队列的阻塞锁和相干同步器的一个同步框架.
根底:
- 线程: 把握 Java 线程
- 锁: 明确什么是锁
- 高并发(可选)
在 Java 新版本 里实现锁的框架
Public class Main {
public static int m = 0;// 动态成员变量
public static void main(String[] args) throws Exception{Thread[] threads = new Thread[100];// 定义一个线程数组
for(int i=0; i<threads.length; i++) {threads[i] = new Thread(()->{for(int j=0; j<100; j++) m++;
});
}
for(Thread t : threads) t.start();// 开始运行所有线程
for(Thread t : threads) t.join();// 期待所有线程完结
System.out.println(m);
}
}
/* 这时候运行的后果并不精确 */
/* 值会小于 10000*/
如果这么写, 运行后果并不精确; 其起因是:
如果 m = 0 始终 ++ 到 100, 当第一个线程 m ++, 另一个线程也拿过去加, 当第一个线程始终加到 99 时返回, 而另一个线程也加到 99 返回, 这样一来 m 原本应该是 + 2 返回, 但只加了 1 就返回 ;
那么如何解决这个问题: 加锁,
加锁概念
是把锁在 Main.class 身上, 锁的是对象, 监听的是对象。
给其加锁后, 当一个线程执行时, 另一个线程也要来执行时, 发现锁被占用, 就会产生阻塞, 直到被锁上的线程执行完。
其余线程在争这把锁, 谁争到谁就执行, 这样就防止了 俩个 线程同时执行。
2.synchronized() 同步锁
- 为什么应用 synchronized 在并发编程中存在线程平安问题, 次要起因:1. 存在共享数据 2. 多线程独特操作共享数据. 关键词 synchronized 能够保障在同一时刻只有一个线程能够执行某个办法或某个代码块, 同时 synchronized 能够保障一个线程的变动可见.
- synchronized 的三种利用形式 一般同步办法(实例), 锁是以后实例对象, 进入同步代码前要取得以后实例的锁。动态同步办法, 锁是以后类的 class 对象, 进入同步代码前要取得以后类对象的锁。同步办法块, 锁是括号外面的对象, 对给定对象加锁, 进入同步代码库前要取得给定对象的锁。
- synchronized 的作用 确保线程互斥拜访同步代码。保障共享变量的批改可能及时可见。解决重排序问题。
例:
Public class Main {
public static int m = 0;// 动态成员变量
public static void main(String[] args) throws Exception{Thread[] threads = new Thread[100];// 定义一个线程数组
for(int i=0; i<threads.length; i++) {threads[i] = new Thread(()->{
/* 这里锁的是对象 */
synchronized (Main.class){for(int j=0; j<100; j++) m++;
}
});
}
for(Thread t : threads) t.start();// 开始运行所有线程
for(Thread t : threads) t.join();// 期待所有线程完结
System.out.println(m);
}
}
3.ReentrantLock() 重入锁
ReentrantLock 底层实现就是 AQS
ReentranLock 个性(比照 synchronized)
- 尝试取得锁
- 获取到锁的线程可能响应中断
例:
Public class Main {
public static int m = 0;// 动态成员变量
public static Lock lock = new ReentrantLock();// 这里定义一把锁
public static void main(String[] args) throws Exception{Thread[] threads = new Thread[100];// 定义一个线程数组
for(int i=0; i<threads.length; i++) {threads[i] = new Thread(()->{
/* 必须要用 try finally*/
try{lock.lock();// 上锁
for(int j=0; j<100; j++) m++;
} finally{lock.unlock();// 解锁
}
});
}
for(Thread t : threads) t.start();// 开始运行所有线程
for(Thread t : threads) t.join();// 期待所有线程完结
System.out.println(m);
}
}
4.CountDownLatch
CountDownLatch 是 Java 中自带的锁, 它的实现也是基于 AQS
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.locks.Lock;
/**
* java 本身带的锁:CountDownLatch
*/
public class CountDown {
public static int m = 0;
public static Lock lock =new Mylock();
public static CountDownLatch latch = new CountDownLatch(100);// 给其一个值, 为 100, 当所有线程完结值为零
public static void main(String[] args) throws InterruptedException {Thread[] threads = new Thread[100];
for (int i = 0; i < threads.length; i++) {threads[i] = new Thread(()->{
try {lock.lock();
for (int j = 0; j < 100; j++) m++;
}finally {lock.unlock();
}
latch.countDown();// 每次调用减一});
}
for (Thread t: threads) t.start();
latch.await();// 期待 latch 关上, 当值变为 0 时关上, 当所有线程执行完后值变为 0
System.out.println(m);
}
}
5. 应用 AQS 自定义锁
AQS 的应用:
在 AQS 外部有一个 int 类型的值:State, 在这个数根底之上治理着一个双向队列, 当有新的线程去拜访这个值的时候, 如果没有竞争不到拜访权, 就在加在这个队列之后
应用 Lock 接口来写实现类, 应用 AQS 来实现锁
import java.util.concurrent.locks.AbstractQueuedSynchronizer;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.AbstractQueuedSynchronizer;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
public class Mylock implements Lock{private Sync sync = new Sync();
@Override
public void lock() {sync.acquire(1);// 调用 acquire, 不是当下写的 Tryacquire
}
@Override
public void lockInterruptibly() throws InterruptedException {}
@Override
public boolean tryLock() {return false;}
@Override
public boolean tryLock(long time, TimeUnit unit) throws InterruptedException {return false;}
@Override
public void unlock() {}
@Override
public Condition newCondition() {return null;}
/* 类名 Sync, 继承 AbstractQueuedSynchronizer*/
public class Sync extends AbstractQueuedSynchronizer{
@Override
/* 重写 tryAcqurie 办法 */
protected boolean tryAcquire(int arg) {
assert arg == 1; // 在 JDKAPI 文档中, 要求要传进来一个 1, 所以用 assert〔〕
if (compareAndSetState(0,1)) {// 这里应用到了 CAS
/*CAS: 判断传进去的值, 如果是 0 将其变为 1 */
// 判断 m 的值, 如果是零将其变为 1
setExclusiveOwnerThread(Thread.currentThread());// 互斥锁
return true;
}
return false;
}
@Override
/* 开释 */
protected boolean tryRelease(int arg) {
assert arg == 1;
/* 判断以后线程是否持有这把锁 */
if(!isHeldExclusively()) throw new IllegalMonitorStateException();
setExclusiveOwnerThread(null);
setState(0);
return true;
}
@Override
protected boolean isHeldExclusively() {return getExclusiveOwnerThread() == Thread.currentThread();// 拿到以后领有的排它的线程和以后线程是否一样}
}
}
这里所介绍的锁都是基于 AQS 来实现.
举荐浏览
字节跳动总结的设计模式 PDF 火了,完整版凋谢分享
刷 Github 时发现了一本阿里大神的算法笔记!标星 70.5K
月薪没有达到 30K 的 Java 程序员 是听不懂这个我的项目的
为什么阿里巴巴的程序员成长速度这么快,看完他们的内部资料我懂了
程序员达到 50W 年薪所须要具备的常识体系。
对于【暴力递归算法】你所不晓得的思路
看完三件事❤️
如果你感觉这篇内容对你还蛮有帮忙,我想邀请你帮我三个小忙:
点赞,转发,有你们的『点赞和评论』,才是我发明的能源。
关注公众号『Java 斗帝』,不定期分享原创常识。
同时能够期待后续文章 ing????