AQS (abstractQueuedSynchroizer)
- 抽象队列同步器
synchronized 1.5的改进
- synchronized是重量级锁。但是在java1.5后对synchronized进行了改进
- synchronized锁在初始是偏向锁,当有其他线程竞争时升级成为轻量级锁,(多数情况下是自旋锁) 当自旋超过10次将会升级成为重量级锁
为什么synchronized是重量级锁
synchronized锁 需要jvm调用操作系统(OS),然后需要cpu在用户态(user mode)跟内核态(kernel mode)来回切换. 比较耗时,所以叫重量级锁
lock锁只需要在jvm中就可以实现锁了,所以是轻量级的
乐观锁cas和悲观锁synchronized锁适合于哪种情况
- 如果在超高并发,锁的竞争特别激烈的情况下,适合于悲观锁。(否则自旋锁一直自旋,消耗cpu)
- 如果并发不高,竞争不激烈,cas比较合适。
lock的实现类 依赖于 AQS
AQS 依赖于一个 先进先出的等待队列 CLH队列
volatile 关键字作用
保证一个线程修改了某个变量的值后,该新值对其他线程是立即可见
禁止进行指令重排序
不保证原子性
使用场景
- 在双重检查单例模式的那里,对象应该加 volatile关键字。
原因: jvm在内部对对象进行初始化的操作时的步骤为:1.开辟空间 2.空间初始化 3.init赋值 4.将地址赋值给变量。
在实际操作中,这四个步骤的执行顺序可能 2执行完成后,先执行了4,然后才执行3.
在多线程的环境下,假如 在执行完4后还没来得急执行3. 另一个线程进入,判断 INSTANCE==null 错误,返回INSTANCE对象,但是对象还没有执行第3步,是个半成品对象。则出现问题。
public class Singleton1 {
//需要加上volatile,为了防止指令重排
private static volatile Singleton1 INSTANCE;
private Singleton1(){
}
public Singleton1 newInstance(){
if(INSTANCE==null) {
synchronized (Singleton1.class) {
if (INSTANCE == null) {
INSTANCE = new Singleton1();
}
}
}
return INSTANCE;
}
}
自实现lock锁 采用 AQS锁机制
- 自定义的lock锁可以将AQS锁作为内部类 来实现乐观锁操作
- AQS内部有一个volatile int型的变量state 用来当锁变量(初始值为0),和一个先进先出的等待队列CLH
- 上锁:lock 锁中的lock 方法 调用 内部类的acquire方法,acquire调用 tryAcquire方法(模板设计模式)
- tryAcquire 调用cas算法,自旋等待 锁被释放。 得到锁后,上锁(state变为1),并设为互斥锁 返回true
- 释放锁:lock锁的unlock方法 调用内部类的release方法, release调用tryRelease方法
- tryRelease判断当前线程是否占有锁,如果占有锁,则释放,并把state再设回 0;
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 AQSLock implements Lock {
private Sync sync=new Sync();
@Override
public void lock() {
//调用 acquire方法, 是模板设计模式
sync.acquire(1);
}
@Override
public void unlock() {
sync.release(1);
}
@Override
public void lockInterruptibly() throws InterruptedException {
}
@Override
public boolean tryLock() {
return false;
}
@Override
public boolean tryLock(long time, TimeUnit unit) throws InterruptedException {
return false;
}
public class Sync extends AbstractQueuedSynchronizer{
/**
* 当新线程来了后,调用此方法 竞争锁资源
* @param arg
* @return
*/
@Override
protected boolean tryAcquire(int arg) {
//AQS内部实现是 一个int变量state作为锁,初始值为0, 和一个先进先出等待队列 CLH
if(compareAndSetState(0,1)){ //如果锁变量由0变为了1。这里调用的是CAS算法
setExclusiveOwnerThread(Thread.currentThread());// 设置为互斥锁
return true;
}
return false;
}
/**
* 尝试释放锁资源
* @param arg
* @return
*/
@Override
protected boolean tryRelease(int arg) {
assert arg==1;// arg必须是1 如果不是1则认为不对
//如果当前线程不持有锁,则抛出异常
if(!isHeldExclusively()) throw new IllegalMonitorStateException();
//持有锁则释放
setExclusiveOwnerThread(null);
setState(0);
return true;
}
/**
* 返回当前占有锁的线程是不是当前线程(当前线程是不是持有锁)
* @return
*/
@Override
protected boolean isHeldExclusively() {
return getExclusiveOwnerThread() ==Thread.currentThread();
}
}
@Override
public Condition newCondition() {
return null;
}
}
测试类:
import java.util.concurrent.locks.Lock;
public class TestLock {
public static int m=0;
public static void main(String[] args) throws InterruptedException {
Thread[] thread =new Thread[100];
Lock lock=new AQSLock();
for (int i = 0; i <100 ; i++) {
thread[i]=new Thread(()->{
try {
lock.lock();
for (int j = 0; j < 100; j++) {
m++;
}
}finally {
lock.unlock();
}
});
}
for (Thread t : thread) {
t.start();
}
for (Thread t : thread) {
t.join();
}
System.out.println(m);
}
}
assert 关键字
assert boolean表达式; 正确 继续执行 ,错误 抛出异常。
CountDownLatch
- 使用 countDownLatch 代替join方法 做门栓。
- CountDownLatch latch=new CountDownLatch(100);
- 也是基于 AQS来实现的
- 内部使用了一个 共享锁
- 调用 countDown方法 使值减一。 当值为1时打开锁。
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.locks.Lock;
public class TestLock {
public static int m=0;
public static void main(String[] args) throws InterruptedException {
Thread[] thread =new Thread[100];
CountDownLatch latch=new CountDownLatch(100);
Lock lock=new AQSLock();
for (int i = 0; i <100 ; i++) {
thread[i]=new Thread(()->{
try {
lock.lock();
for (int j = 0; j < 100; j++) {
m++;
}
}finally {
lock.unlock();
}
});
latch.countDown();
}
for (Thread t : thread) {
t.start();
}
latch.await();
// for (Thread t : thread) {
// t.join();
// }
System.out.println(m);
}
}