AQS(队列同步器AbstractQueuedSynchronizer):
AQS使用了模板方法 设计模式 实现AQS 需要继承 AbstractQueuedSynchronizer。最少实现tryAcquire 与 tryRelease
/**
* The synchronization state.
aqs里的关键成员变量 通过检查与修改state状态来实现同步状态的更改
*/
private volatile int state;
•getState():获取当前同步状态。
•setState(int newState):设置当前同步状态。
•compareAndSetState(int expect,int update):使用CAS设置当前状态,该方法能够保证状态设置的原子性。
-
AbstractQueuedSynchronizer中的模板方法:
这些模板方法同步器提供的模板方法基本上分为3类:独占式获取与释放同步状态、共享式获取与释放、同步状态和查询同步队列中的等待线程情况。
-
可重写的方法
实现:
- 不可重入锁
//实现独占锁 不可重入
public class SelfLock implements Lock {
private static Sync sync = new Sync();
private static class Sync extends AbstractQueuedSynchronizer{
protected boolean isHeldExclusively(){return getState()==1;}
@Override
protected boolean tryAcquire(int arg) {
if(compareAndSetState(0,1)){
setExclusiveOwnerThread(Thread.currentThread());
return true;
}
return false;
}
@Override
protected boolean tryRelease(int arg) {
if(getState()==0){
throw new IllegalMonitorStateException();
}
setExclusiveOwnerThread(null);
setState(0);
return true;
}
ConditionObject newCondition(){
return new ConditionObject();
}
}
@Override
public void lock() {
sync.acquire(1);
}
@Override
public void unlock() {
sync.release(1);
}
@Override
public void lockInterruptibly() throws InterruptedException {
sync.acquireSharedInterruptibly(1);
}
@Override
public boolean tryLock() {
return sync.tryAcquire(1);
}
@Override
public boolean tryLock(long timeOut, TimeUnit unit) throws InterruptedException {
return sync.tryAcquireNanos(1,unit.toNanos(timeOut));
}
@Override
public Condition newCondition() {
return sync.newCondition();
}
}
//自实现锁测试
public class TestMyLock {
public void test() {
final Lock lock = new SelfLock();
class Worker extends Thread {
public void run() {
lock.lock();
System.out.println(Thread.currentThread().getName());
try {
SleepTools.second(1);
} finally {
lock.unlock();
}
}
}
// 启动4个子线程
for (int i = 0; i < 4; i++) {
Worker w = new Worker();
//w.setDaemon(true);
w.start();
}
// 主线程每隔1秒换行
for (int i = 0; i < 10; i++) {
SleepTools.second(1);
//System.out.println();
}
}
public static void main(String[] args) {
TestMyLock testMyLock = new TestMyLock();
testMyLock.test();
}
}
- 可重入锁
//实现可重入锁
public class SelfReentrantLock implements Lock {
private static Sync sync = new Sync();
private static class Sync extends AbstractQueuedSynchronizer{
protected boolean isHeldExclusively(){return getState()==1;}
@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;
}
ConditionObject newCondition(){
return new ConditionObject();
}
}
@Override
public void lock() {
System.out.println(Thread.currentThread().getName()+" ready get lock");
sync.acquire(1);
System.out.println(Thread.currentThread().getName()+" already got lock");
}
@Override
public void unlock() {
System.out.println(Thread.currentThread().getName()+" ready release lock");
sync.release(1);
System.out.println(Thread.currentThread().getName()+" already released lock");
}
@Override
public void lockInterruptibly() throws InterruptedException {
sync.acquireSharedInterruptibly(1);
}
@Override
public boolean tryLock() {
return sync.tryAcquire(1);
}
@Override
public boolean tryLock(long timeOut, TimeUnit unit) throws InterruptedException {
return sync.tryAcquireNanos(1,unit.toNanos(timeOut));
}
@Override
public Condition newCondition() {
return sync.newCondition();
}
}
//使用了不可重入锁
public class UseReenTrantLock {
private SelfLock reentrantLock=new SelfLock();
private int count = 0;
private synchronized void method(){
reentrantLock.lock();
try {
System.err.println("当前线程"+Thread.currentThread().getName()+"进入。。。");
count++;
Thread.sleep(2000);
System.out.println(count);
System.err.println("当前线程"+Thread.currentThread().getName()+"退出。。。");
} catch (InterruptedException e) {
e.printStackTrace();
}finally{
reentrantLock.unlock();
}
}
public static void main(String[] args){
final UseReenTrantLock useReenTrantLock=new UseReenTrantLock();
Thread t1=new Thread(new Runnable() {
@Override
public void run() {
useReenTrantLock.method();
}
},"t1");
Thread t2=new Thread(new Runnable() {
@Override
public void run() {
useReenTrantLock.method();
}
},"t2");
t1.start();
t2.start();
}
}
//使用可重入锁
public class TestReenterSelfLock {
static final Lock lock = new SelfReentrantLock();
public void reenter(int x){
lock.lock();
try {
System.out.println(Thread.currentThread().getName()+":递归层级:"+x);
int y = x - 1;
if (y==0) return;
else{
reenter(y);
}
} finally {
lock.unlock();
}
}
public void test() {
class Worker extends Thread {
public void run() {
System.out.println(Thread.currentThread().getName());
SleepTools.second(1);
reenter(3);
}
}
// 启动3个子线程
for (int i = 0; i < 3; i++) {
Worker w = new Worker();
w.start();
}
// 主线程每隔1秒换行
for (int i = 0; i < 100; i++) {
SleepTools.second(1);
}
}
public static void main(String[] args) {
TestReenterSelfLock testMyLock = new TestReenterSelfLock();
testMyLock.test();
}
}
-
CLH队列锁:
CLH队列锁也是一种基于链表的可扩展、高性能、公平的自旋锁,申请线程仅仅在本地变量上自旋,它不断轮询前驱的状态,假设发现前驱释放了锁就结束自旋。
每次要拿锁时,先加入获取队列。一直不断的自旋队列的上一个节点是否获得锁了,自旋到一定次数后阻塞进入等候队列
当一个线程需要获取锁时:
1.创建一个的QNode,将其中的locked设置为true表示需要获取锁,myPred表示对其前驱结点的引用
2.线程A对tail域调用getAndSet方法,使自己成为队列的尾部,同时获取一个指向其前驱结点的引用myPred
线程B需要获得锁,同样的流程再来一遍
3.线程就在前驱结点的locked字段上旋转,直到前驱结点释放锁(前驱节点的锁值 locked == false)
4.当一个线程需要释放锁时,将当前结点的locked域设置为false,同时回收前驱结点
volatie原理
-
在说volatie原理之前先来说下JMM(Java memory mode)
在计算机系统中,寄存器划是L0级缓存,接着依次是L1,L2,L3(接下来是内存,本地磁盘,远程存储)。越往上的缓存存储空间越小,速度越快,成本也更高;越往下的存储空间越大,速度更慢,成本也更低。从上至下,每一层都可以看做是更下一层的缓存,即:L0寄存器是L1一级缓存的缓存,L1是L2的缓存,依次类推;每一层的数据都是来至它的下一层,所以每一层的数据是下一层的数据的子集。
每个线程都会分配有一个工作内存,同时共享一个主内存。线程不能直接访问主内存,只能操作工作内存。在读取变量会从主内存读取到自己的工作内存里进行操作。
-
实现原理
volatile关键字修饰的变量会存在一个“lock:”的前缀。
Lock前缀,Lock不是一种内存屏障,但是它能完成类似内存屏障的功能。Lock会对CPU总线和高速缓存加锁,可以理解为CPU指令级的一种锁。
同时该指令会将当前处理器缓存行的数据直接写会到系统内存中,且这个写回内存的操作会使在其他CPU里缓存了该地址的数据无效。
所以其他线程需要重新从主内存读取
syncionzerd原理
Synchronized在JVM里的实现都是基于进入和退出Monitor对象来实现方法同步和代码块同步,虽然具体实现细节不一样,但是都可以通过成对的MonitorEnter和MonitorExit指令来实现
对同步块,MonitorEnter指令插入在同步代码块的开始位置,而monitorExit指令则插入在方法结束处和异常处,JVM保证每个MonitorEnter必须有对应的MonitorExit。总的来说,当代码执行到该指令时,将会尝试获取该对象Monitor的所有权,即尝试获得该对象的锁:
1、如果monitor的进入数为0,则该线程进入monitor,然后将进入数设置为1,该线程即为monitor的所有者。
2、如果线程已经占有该monitor,只是重新进入,则进入monitor的进入数加1.
3.如果其他线程已经占用了monitor,则该线程进入阻塞状态,直到monitor的进入数为0,再重新尝试获取monitor的所有权。
对同步方法,从同步方法反编译的结果来看,方法的同步并没有通过指令monitorenter和monitorexit来实现,相对于普通方法,其常量池中多了ACC_SYNCHRONIZED标示符。
JVM就是根据该标示符来实现方法的同步的:当方法被调用时,调用指令将会检查方法的 ACC_SYNCHRONIZED 访问标志是否被设置,如果设置了,执行线程将先获取monitor,获取成功之后才能执行方法体,方法执行完后再释放monitor。在方法执行期间,其他任何线程都无法再获得同一个monitor对象。
synchronized使用的锁是存放在Java对象头里面,Java对象的对象头由 mark word 和 klass pointer 两部分组成:
1)mark word存储了同步状态、标识、hashcode、GC状态等等。
2)klass pointer存储对象的类型指针,该指针指向它的类元数据
锁的状态与优化
一共有四种状态,无锁状态,偏向锁状态,轻量级锁状态和重量级锁状态,它会随着竞争情况逐渐升级。锁可以升级但不能降级,目的是为了提高获得锁和释放锁的效率。
1.偏向锁,顾名思义,它会偏向于第一个访问锁的线程,如果在运行过程中,同步锁只有一个线程访问,不存在多线程争用的情况,则线程是不需要触发同步的,减少加锁/解锁的一些CAS操作(比如等待队列的一些CAS操作),这种情况下,就会给线程加一个偏向锁。
偏向锁:是指线程在操作资源之前 直接检查 上次 获得锁的对象是不是当前要操作的线程如果是直接操作资源
2.轻量级锁:存在多个进程竞争锁时,不直接加锁而是通过cas操作来看是否能够拿到锁 通常周期为1个上下文切换时间周期。(自适应自旋锁) 在从偏向锁切换到轻量级锁 还会进行一次线程暂停所以工作线程 gc工作(stop thte world)
3.轻量级锁超过一个切换上下文周期还是拿不到锁就会膨胀为重量级锁 此时线程会进入阻塞-
不同状态对象头里记录的不同信息:
-
锁的升级过程