1.AQS 介绍
AQS中维护了一个volatile int state(共享资源)和一个CLH队列。当state=1时代表当前对象锁已经被占用,其他线程来加锁时则会失败,失败的线程被放入一个FIFO的等待队列中,然后会被 UNSAFE.park() 操作挂起,等待已经获得锁的线程释放锁才能被唤醒。 一般常用的ReentrantLock,CountDownLatch,Semaphore 都有使用到AQS;
2.手动实现独占锁和重入锁
package com.company.test.lock;
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 ReenterLock implements Lock {
private static class Sync extends AbstractQueuedSynchronizer {
@Override
protected boolean tryAcquire(int arg) {
if (compareAndSetState(0, 1)) {
setExclusiveOwnerThread(Thread.currentThread());
return true;
} else if (getExclusiveOwnerThread() == Thread.currentThread()) {
setState(getState() + 1);
return true;
}
return false;
}
@Override
protected boolean tryRelease(int arg) {
if (getExclusiveOwnerThread() != Thread.currentThread()) {
throw new IllegalMonitorStateException();
}
if (getState() == 0) {
throw new IllegalMonitorStateException();
}
setState(getState() - 1);
if (getState() == 0) {
setExclusiveOwnerThread(null);
}
return true;
}
@Override
protected boolean isHeldExclusively() {
return getState() > 0;
}
public ConditionObject newCondition() {
return new ConditionObject();
}
}
private final Sync sync = new Sync();
@Override
public void lock() {
sync.acquire(1);
}
@Override
public void lockInterruptibly() throws InterruptedException {
sync.acquireInterruptibly(1);
}
@Override
public boolean tryLock() {
return sync.tryAcquire(1);
}
@Override
public boolean tryLock(long time, TimeUnit unit) throws InterruptedException {
return sync.tryAcquireNanos(1, unit.toNanos(time));
}
@Override
public void unlock() {
sync.release(1);
}
@Override
public Condition newCondition() {
return sync.newCondition();
}
}
测试
package com.company.test.lock;
public class LockTest {
public static void main(String[] args) {
ReenterLock lock = new ReenterLock();
Runnable runnable = new Runnable() {
@Override
public void run() {
lock.lock();
System.out.println(Thread.currentThread().getName() + "getLock");
printName();
System.out.println(Thread.currentThread().getName() + "unLock");
lock.unlock();
}
};
Thread thread1 = new Thread(runnable);
Thread thread2 = new Thread(runnable);
Thread thread3 = new Thread(runnable);
final Thread thread4 = new Thread(runnable);
thread1.start();
thread2.start();
thread3.start();
thread4.start();
}
public static void printName() {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"end task");
}
}
输出结果
Thread-2getLock
Thread-2end task
Thread-2unLock
Thread-3getLock
Thread-3end task
Thread-3unLock
Thread-0getLock
Thread-0end task
Thread-0unLock
Thread-1getLock
Thread-1end task
Thread-1unLock
可以发现,锁机制成功
3.JMM
java 的线程模型,区分别为主内存和工作内存. 主内存和工作内存是java模型里面的逻辑概念,工作内存主要是CPU 寄存器和L1,L2,L3 等高速缓存,主内存主要是设备的物理内存. 线程读取时候都是从主内存把数据读取到自己的工作内存,修改后再次写入主内存. 线程的各个工作内存相互间不能相互访问. 一般认为CPU 是计算逻辑单元,对于CPU来说,从主内存中读取数据占用的时间,要远远大于读取完毕之后计算数据的时间.
4.volatile
volatile 是JVM里面最轻量级的线程同步工具,最适合一个线程写入数据,多个线程读取数据的"一写多读"情况. volatile 能保证可见性和有序性.用volatile 标注的变量,能及时的写入主内存,并使得其他工作线程的工作内存副本失效,再次使用时候需要读取主内存的值. 使用volatile 也能保证指令重排的有序性. 在汇编语言里面有lock 内存屏障,保证了有序性和可见性.