多线程中的常见锁分类

锁分类
公平锁和非公平锁
公平锁:是指多个线程按照申请锁的顺序来获取锁

非公平锁:是指多个线程获取锁的顺序并不是按照申请锁的顺序,有可能后申请的线程比先申请的线程优先获锁。

非公平锁一上来就尝试占用锁,如果尝试占用失败,就采用公平锁的方式到末尾排队。

在高并发的情况下,有可能造成优先级反转或饥饿现象

非公平锁的优点在于吞吐量比公平锁大。

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();
        }
    }


}
原文链接:https://blog.csdn.net/qq_41282026/article/details/98304960

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值