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{
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{
/*这里锁的是对象*/
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{
/*必须要用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来实现.
推荐阅读
推荐阅读:
刷Github时发现了一本阿里大神的算法笔记!标星70.5K
为什么阿里巴巴的程序员成长速度这么快,看完他们的内部资料我懂了
看完三件事❤️
如果你觉得这篇内容对你还蛮有帮助,我想邀请你帮我三个小忙:
点赞,转发,有你们的 『点赞和评论』,才是我创造的动力。
关注公众号 『 Java斗帝 』,不定期分享原创知识。
同时可以期待后续文章ing