1.公平锁
多个线程按照申请锁的顺序来获取锁。
按照FIFO(先进先出)规则来获取锁。
2.非公平锁
多个线程获取锁的顺序不是按照申请锁的顺序,有可能后申请的线程优先获得锁。
在高并发的情况下,那么就有可能造成优先级反转或饥饿现象。
3.公平锁和非公平锁的获取
并发包中java.util.concurrent.locks.ReentrantLock的创建,可以通过构造函数的布尔值来获取公平锁和非公平锁,默认是非公平锁的。
//java.util.concurrent.locks.ReentrantLock类中
//无参构造,是非公平锁的
public ReentrantLock() {
sync = new NonfairSync();
}
//通过boolean来获取公平锁或非公平锁
//true为公平锁,false为非公平锁
public ReentrantLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();
}
4.可重入锁(递归锁)
同一线程外层函数获得锁后,内层递归函数依然能获取该锁。
同一线程在外层方法获取锁的时候,在进入内层方法会自动获取锁。
即:线程可以进入任何一个它已经拥有的锁所同步的代码块。
- ReentrantLock、Synchronized就是典型的可重入锁。
- 可重入锁最大作用:避免死锁。
5.自旋锁
尝试获取锁的线程不会立即阻塞,而是采用循环的方式去尝试获取锁。
好处:减少线程上下文切换的消耗
缺点:循环获取会消耗CPU
package cn.chen.demo.lockdemo;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
/**
*
* @ClassName: SpinLockDemo
* @Description: 自旋锁的实现
* @author: chenlf
* 1.原理:循环比较获取直到成功为止,没有类似wait阻塞
* 2.通过CAS操作完成自旋锁
*
*/
public class SpinLockDemo {
//原子引用线程
AtomicReference<Thread> atomicThread = new AtomicReference<>();
//上锁
public void myLock() {
//当前线程对象
Thread thread = Thread.currentThread();
System.out.println("当前线程:" + Thread.currentThread().getName()+" 获取锁成功!");
//比较当前对象,期望值是null,如果atomicThread为null,则将当前线程thread设置进去。
//此处满足条件,atomicThread.compareAndSet(null, thread)返回值为true
//取反,因此第一次并没有进while循环体
while (!atomicThread.compareAndSet(null, thread)) {
}
}
//解锁
public void myUnLock() {
//当前线程对象
Thread thread = Thread.currentThread();
//解锁时变成null
atomicThread.compareAndSet(thread, null);
System.out.println("当前线程:" + Thread.currentThread().getName()+" 释放锁成功!");
}
public static void main(String[] args) {
SpinLockDemo spinLock = new SpinLockDemo();
//线程A
new Thread(()->{
spinLock.myLock();//上锁
try { TimeUnit.SECONDS.sleep(5);} catch (Exception e) { e.printStackTrace(); }//暂停线程占用锁5秒钟
spinLock.myUnLock();//解锁
},"线程A" ).start();
//为了确保线程A先获取锁,暂停2秒钟
try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); }
//线程B
new Thread(()->{
spinLock.myLock();//上锁
try { TimeUnit.SECONDS.sleep(2); } catch (InterruptedException e) { e.printStackTrace(); }
spinLock.myUnLock();//解锁
},"线程B" ).start();
}
/**
* 运行结果:
当前线程:线程A 获取锁成功!
当前线程:线程B 获取锁成功!
当前线程:线程A 释放锁成功!
当前线程:线程B 释放锁成功!
B需要等A用完锁才能用锁
*/
}
6.读写锁
独占锁:锁一次只能被一个线程所持有。ReentrantLock和Synchronized均是独占锁
共享锁:锁可以被多个线程所持有。
ReentrantReadWritrLock 其读锁是共享锁,写锁则为独占锁。
读锁的共享锁可以保证并发读,效率非常高。读写,写读,写写的过程为互斥的。
package cn.chen.demo.lockdemo;
/**
*
* @ClassName: ReentrantReadWriteLockDemo
* @Description: 读写锁
* @author: chenlf
* 读:多个线程可以同时读取一个资源类。
* 写:如果有一个线程想去写共享资源,那么其他线程就无法对该资源进行读或写
* 因此,读读可以共存,而读写、写写则是互斥的。
*/
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantReadWriteLock;
/**
*
* @ClassName: MyCache
* @Description:模拟缓存
* @author: chenlf
*
*/
class MyCache{//资源类
//保证可见性、及时性:一有更新立即通知
private volatile Map<String,Object> map = new HashMap<>();
//读写说
private ReentrantReadWriteLock rwLock = new ReentrantReadWriteLock();
/**
* @Title: put
* @Description: 写操作
* @author: chenlf
*/
public void put(String key, Object value) {
rwLock.writeLock().lock();//写锁
try {
System.out.println(Thread.currentThread().getName()+" 正在写入:"+key+"="+value);
//暂停线程占用锁
try { TimeUnit.MILLISECONDS.sleep(300);} catch (Exception e) { e.printStackTrace(); }
map.put(key, value);
System.out.println(Thread.currentThread().getName()+" 写入完成:"+key+"="+value);
} catch (Exception e) {
e.printStackTrace();
} finally {
rwLock.writeLock().unlock();//释放锁
}
}
/**
* @Title: get
* @Description: 读操作
* @author: chenlf
*/
public void get(String key) {
rwLock.readLock().lock();
try {
System.out.println(Thread.currentThread().getName()+" 正在读取:"+key);
try { TimeUnit.MILLISECONDS.sleep(300);} catch (Exception e) { e.printStackTrace(); }
Object value = map.get(key);
System.out.println(Thread.currentThread().getName()+" 读取完成:"+value);
} catch (Exception e) {
e.printStackTrace();
} finally {
rwLock.readLock().unlock();
}
}
/**
*
* @Title: clearMap
* @Description: 清除缓存
* @author: chenlf
*/
public void clearMap(){
map.clear();
}
}
public class ReentrantReadWriteLockDemo {
public static void main(String[] args) {
MyCache myCache = new MyCache();
//模拟写操作
for (int i = 0; i <= 5; i++) {
final int tempInt = i;
new Thread(()-> {
myCache.put(tempInt+"", tempInt+"");
},String.valueOf(i)).start();
}
//模拟读操作
for (int i = 0; i <= 5; i++) {
final int tempInt = i;
new Thread(()-> {
myCache.get(tempInt+"");
},String.valueOf(i)).start();
}
}
//写不被打断,读不做限制
}