1、公平锁和非公平锁
公平锁 :多个线程申请锁的顺序来获取锁(先来后到)
非公平锁 :多个线程通过争抢来获得锁(获取是不按照顺序的)
synchronized,是一个非公平锁
java 中 ReentrantLock 采用不同i的构造方法 可以是公平锁 也可以是非公平锁
/**
默认的非公平锁
*/
public ReentrantLock() {
sync = new NonfairSync();
}
可以通过有参构造 创建一个公平锁
public ReentrantLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();
}
在性能上 : 非公平锁的优点在于吞吐量比公平锁大
可重入锁(递归锁)
ReentrantLock 和 synchronized 都是可重入锁
一、什么是可重入锁 ?
线程可以进入任何一个他已经拥有的锁所同步着的代码块
采用synchronized演示
/**
* 资源类
*/
class Resource{
public synchronized void reentrySynchronizedDemo(){
System.out.println("拿到锁进入");
reentrySynchronized();
}
public synchronized void reentrySynchronized(){
System.out.println("重入锁");
}
}
public class ReentryLockDemo {
public static void main (String[] args) {
Resource resource = new Resource();
resource.reentrySynchronizedDemo();
}
}
接下来是采用显示锁Lock 演示
/**
* 资源类
*/
class Resource{
Lock lock = new ReentrantLock();
/**
* Lock重入锁案例
*/
public void reentryLockDemo(){
//上锁
lock.lock();
try {
System.out.println("拿到锁进入");
reentryLock();
} catch (Exception e) {
e.printStackTrace();
}finally {
lock.unlock();//释放锁
}
}
public void reentryLock(){
//上锁
lock.lock();
try {
System.out.println("重入锁");
} catch (Exception e) {
e.printStackTrace();
}finally {
lock.unlock();//释放锁
}
}
}
public class ReentryLockDemo {
public static void main (String[] args) {
Resource resource = new Resource();
resource.reentryLockDemo();
}
}
运行结果
3、自旋锁
一、什么是自旋锁 ?
就是 尝试获得锁的线程,拿不到锁不会立刻阻塞,而是采取循环的方式去尝试换取锁
优点:可以减少线程的上下文切换
缺点:循环会消耗cpu
例如cas的实现就是采用了自旋锁
public final int getAndAddInt(Object var1, long var2, int var4) {
int var5;
do {
var5 = this.getIntVolatile(var1, var2);
} while(!this.compareAndSwapInt(var1, var2, var5, var5 + var4));
return var5;
}
手写一个自旋锁
/**
* 自旋锁案例
*/
public class SpinLockDemo {
//原子引用
AtomicReference<Thread> atomicReference = new AtomicReference<>();
public void myLock(){
Thread thread = Thread.currentThread();//当前线程
System.out.println(thread.getName() + "come in");
//开始自旋锁
while(!atomicReference.compareAndSet(null,thread));
}
public void unlock(){
Thread thread = Thread.currentThread();//当前线程
atomicReference.compareAndSet(thread,null);
System.out.println(thread.getName() + "come out");
}
public static void main (String[] args) throws InterruptedException {
SpinLockDemo spinLockDemo = new SpinLockDemo();
new Thread(()->{
spinLockDemo.myLock();
System.out.println("t1霸占获取锁");
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("t1释放获取锁");
spinLockDemo.unlock();
},"t1").start();
//睡一秒 保证t1先t2
Thread.sleep(1000);
new Thread(()->{
System.out.println("t2尝试自旋获取锁");
spinLockDemo.myLock();
System.out.println("t2成功获取到锁");
spinLockDemo.unlock();
},"t2").start();
}
}
结果
独占锁(写锁)
指锁一次只能被一个线程持有,ReentrantLock 和synchronized 都是独占锁
共享锁(读锁)
指该锁可以被多个线程占有
读写锁
ReentrantReadWriteLock 其读锁是共享锁 ,写锁是独占锁
优点:可以保证并发读是非常高效的,读写,写写,写读过程都是互斥的
案例
/**
* 读写锁案例
*/
public class ReadWriteDemo {
public static void main (String[] args) {
Cache cache = new Cache();
//写的时候只有一个线程独占锁 互斥其他
for (int i = 0; i < 5; i++) {
new Thread(()->{
cache.write();
},i+"").start();
}
//并发读,读锁只有一把,所有线程共享
for (int i = 0; i < 200; i++) {
new Thread(()->{
cache.read();
},i+"").start();
}
}
}
class Cache{
ReentrantReadWriteLock reentrantReadWriteLock = new ReentrantReadWriteLock();
public void read() {
//上读锁
reentrantReadWriteLock.readLock().lock();
try {
System.out.println(Thread.currentThread().getId() + "正在读");
} catch (Exception e) {
e.printStackTrace();
}finally {
System.out.println(Thread.currentThread().getId() + "读完");
reentrantReadWriteLock.readLock().unlock();
}
}
public void write() {
//上读锁
reentrantReadWriteLock.writeLock().lock();
try {
System.out.println(Thread.currentThread().getId() + "正在写");
Thread.sleep(1000);
System.out.println(Thread.currentThread().getId() + "睡1s");
} catch (Exception e) {
e.printStackTrace();
}finally {
reentrantReadWriteLock.writeLock().unlock();
}
}
}