JAVA——锁系列

文章目录

      • 一、synchronized
        • 1、原理
        • 2、用法
        • 3、特性
          • (1)锁升级
          • (2)锁消除
          • (3)锁粗化
          • (4)线程之间的通信
      • 二、Lock
        • 1、原理
          • (1)、什么是AQS?
          • (2)、AQS的工作流程
        • 2、用法
        • 3、特性
          • (1)、非公平/非公平锁
            • 声明
            • 底层
          • (2)、可重入性
          • (3)、等待超时
          • (4)、线程间的通信
      • 三、synchronized和ReetrantLock的区别(原理、用法、特性)

一、synchronized

1、原理

synchronized是jvm层面的一把互斥锁,它的底层是给对象加锁,根据对象头的锁标志位进行加锁。使用synchronized进行代码块加锁后,会在代码块编译的字节码前后生成monitorEnter和monitorExit执行,执行monitorEnter表示加锁,执行monitorExit表示释放锁。

保证线程的:原子性、可见性、有序性

2、用法

(1)修饰代码块,使用用户自定义的对象加锁。
(2)修饰普通方法,锁住的是this对象。
(3)修饰静态方法,锁住的是class对象。

3、特性

(1)锁升级

在jdk1.4后,synchronized支持锁升级,升级的过程是不可逆的。
无锁
偏向锁
在没有其它线程竞争资源的情况下,一个线程访问资源时可加偏向锁,偏向锁避免线程获取锁时的资源竞争。
加锁的流程:
(1)先判断对象头的mark world中是否开启偏向锁。
(2)若开启偏向锁,则判断mark world中的线程id是否指向当前线程,如果是,则代表获取锁成功,开始执行同步代码块。
(3)若mark world中的线程id没有不是指向当前线程,则代表有竞争,就进行CAS竞争锁,如果竞争成功,就将mark world中的线程id设置为当前线程id,然后去执行同步代码块。
(4)如果CAS竞争锁失败,则升级为轻量级锁。
轻量级锁(自旋)
在竞争较少的情况下,轻量级锁可以避免使用线程阻塞和唤醒带来的性能影响,轻量级锁采用自旋的方式,让竞争锁的线程原地循环等待,等待其他线程释放锁后再获取锁。为避免等待时间过长而带来的CPU消耗,一般会设置自旋次数(默认10次),可以通过-XX:PreBlockSpin进行设置。当自旋次数操作阈值就会升级为重量级锁。
可重入锁
同一线程可多次获取锁。比如在代码块中多次加锁,每次加锁都会匹配一对monitorEnter和monitorExit。执行monitorEnter表示加锁,执行monitorExit表示释放锁。
非公平

(2)锁消除

是指在编译时期,编译器会消除加了锁,但是并不会有竞争的锁。

(3)锁粗化

是在jvm会根据对象加锁的频次适当扩大加锁的范围,如下代码:

for(int i=0;i<1000000;i++){
	synchronized(LOCK){
		//同步代码块
	}
}

以上代码经过锁粗化后为:

synchronized(LOCK){
	for(int i=0;i<1000000;i++){
	}
}
(4)线程之间的通信

使用wait()、notify()/notifyAll()方法。

二、Lock

1、原理

lock是java juc包下的API,其底层是基于AQS和CAS实现的。

(1)、什么是AQS?

AQS是一个抽象队列同步器。它的底层设计是维护了一个共享资源字段state,该字段使用volatile修饰;还维护了一个先进先出队列(FIFO),该队列是一个双向队列,该队列用来存储竞争共享资源的线程,使线程排队有序的访问共享资源。AQS的资源共享的方式有Exclusive(独占)Share(共享)。独占式的实现案例如ReentrantLock,共享式的实现案例如CountDownLatch/Semaphore。

(2)、AQS的工作流程

当多个线程竞争共享资源的时候,aqs先将线程存依次存储在队列中,然后头结点的线程尝试去获取state字段的值(0表示无锁,大于1表示加锁),如果state=0,则使用CAS尝试去更改state=1,更改成功则表示加锁成功,则该线程就可以去访问共享资源,解锁时使用CAS去递减state的值。然后头结点指向下一个线程所在的节点,如此流程循环,保证有序的获取锁。

2、用法

// 初始化锁
Lock lock = new ReentrantLock();
// 加锁
lock.lock();
业务代码。。。。
// 解锁
lock.unlock();

3、特性

(1)、非公平/非公平锁
声明

是指ReentrantLock在实现的时候实现了公平锁和非公平锁:

// 默认非公平锁
Lock lock =  new ReentrantLock();// 内部为new NonfairSync();
// 公平锁
Lock lock = new ReentrantLock(true);// 内部实现为new FairSync()
底层

公平锁和非公平锁在底层的区别是指在入队列前是否会尝试获取锁。非公平锁在入队列前会去尝试获取一次锁,公平锁则是直接进入队列排队。由于非公平锁可能会减少入队列出队列的次数,所以非公平锁在性能上会好于公平锁。

(2)、可重入性

指同一线程多次加锁,这时会判断锁是否被该线程持有,若持有,则直接将state加1,多次加锁则累加1;解锁则递减。加锁代码如下:
JAVA——锁系列_第1张图片

(3)、等待超时

这里指获取锁时可以设置获取锁时等待的超时时间,使用tryLock(10000);

(4)、线程间的通信

使用Condition的await()和signal()方法实现精确唤醒某一个线程,相比synchronized,减少不必要的资源竞争。

三、synchronized和ReetrantLock的区别(原理、用法、特性)

你可能感兴趣的:(JAVA,基础总结复习,java,面试)