锁分类
公平锁和非公平锁
公平锁:是指多个线程按照申请锁的顺序来获取锁
非公平锁:是指多个线程获取锁的顺序并不是按照申请锁的顺序,有可能后申请的线程比先申请的线程优先获锁。
非公平锁一上来就尝试占用锁,如果尝试占用失败,就采用公平锁的方式到末尾排队。
在高并发的情况下,有可能造成优先级反转或饥饿现象
非公平锁的优点在于吞吐量比公平锁大。
ReentrantLock:可以指定构造方法的boolean类型来指定是公平锁还是非公平锁,默认是非公平锁
synchronized:是一种非公平锁
可重入锁(又名递归锁)
可重入锁:指的是同一线程外层方法获得锁之后,内层递归方法仍然能够获得该锁的代码
在同一个线程在外层方法获取锁的时候,在进入内层方法的时候会自动获取锁
也就是说,线程可以进入任何一个它自己已经拥有的锁所同步着的代码块
可重入锁的最大作用是避免死锁
ReentrantLock和synchronized就是一个典型的可重入锁
class People{
Lock lock = new ReentrantLock();
public void get(){
lock.lock();
try{
System.out.println(Thread.currentThread().getName()+" get");
set();
}finally {
lock.unlock();
}
}
public void set(){
lock.lock();
try{
System.out.println(Thread.currentThread().getName()+" set");
}finally {
lock.unlock();
}
}
}
public class ReentrantLockDemo {
public static void main(String[] args) throws InterruptedException {
People people = new People();
new Thread(()->{
people.get();
},"t3").start();
new Thread(()->{
people.get();
},"t4").start();
}
}
自旋锁
是指尝试获取锁的线程不会立即被阻塞,而是采用循环的方式去尝试获取锁,这样的好处是减少线上下文切换的消耗,缺点是循环会消耗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 Spinlock {
//原子引用线程
AtomicReference<Thread> atomicReference = new AtomicReference<>();
//CAS上锁,获取锁后将自己扔到原子引用类中
public void myLock(){
Thread thread = Thread.currentThread();
System.out.println(thread.getName()+"\t come in ...");
while(!atomicReference.compareAndSet(null,thread)){}
}
//解锁,将原子引用类改回null
public void myUnlock(){
Thread thread = Thread.currentThread();
while(atomicReference.compareAndSet(thread,null)){
}
System.out.println(thread.getName()+"\t come out ...");
}
public static void main(String[] args) throws InterruptedException {
//锁
Spinlock spinlock = new Spinlock();
new Thread(()->{
AAA线程获的锁
spinlock.myLock();
//AAA干5s的事
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
//AAA解锁
spinlock.myUnlock();
},"AAA").start();
TimeUnit.SECONDS.sleep(1);
new Thread(new Runnable() {
@Override
public void run() {
//BBB获取锁,一直在循环获得锁,AAA释放锁后,停止循环,获得锁
spinlock.myLock();
//BBB解锁
spinlock.myUnlock();
}
},"BBB").start();
}
}
独占锁
是指该锁一次只能被一个线程所持有。
ReentrantLock和synchronized都是独占锁
共享锁
是指该锁可以被多个线程所持有
ReentrantReadWriteLock:其读锁是共享锁,其写锁是独占锁
读锁的共享锁可保证并发读是非常高效的,读写、写读、写写的过程是互斥的
读写锁演示
public class ReadWriteLock {
public static void main(String[] args) {
Resource resource = new Resource();
//5个线程进行写入
for(int i = 1;i <= 5;i++){
final int tem = i;
new Thread(()->{
resource.putV(tem+"",tem+"");
},"put-"+i).start();
}
//5个线程进行读取
for(int i = 1;i <= 5;i++){
final int tem = i;
new Thread(()->{
resource.getV(tem+"");
},"get-"+i).start();
}
}
}
class Resource{
public volatile Map<String,String> map = new HashMap<>();
ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
//写入操作
public void putV(String key,String value){
try{
lock.writeLock().lock();
String name = Thread.currentThread().getName();
System.out.println(name + "\t 开始写入值..." + "\t "+key);
TimeUnit.MILLISECONDS.sleep(300);
map.put(key,value);
System.out.println(Thread.currentThread().getName() + "\t 完成写入值...");
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.writeLock().unlock();
}
}
//读取操作
public void getV(String key) {
try{
lock.readLock().lock();
String name = Thread.currentThread().getName();
System.out.println(name + "\t 开始读取值...");
Object result = map.get(key);
TimeUnit.MILLISECONDS.sleep(300);
System.out.println(Thread.currentThread().getName() + "\t 完成读取值..." + "\t "+result);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.readLock().unlock();
}
}
}