公平/非公平锁/可重入锁/自旋锁

公平锁
是指多个线程按照申请锁的顺序来获取锁类似排队打饭 先来后到

非公平锁
是指在多线程获取锁的顺序并不是按照申请锁的顺序,有可能后申请的线程比先申请的线程优先获取到锁,在高并发的情况下,有可能造成优先级反转或者饥饿现象

日常见到的比较多的两种锁,也就是synchronized/ReentrantLock,synchronized是非公平锁,而ReentrantLock可以通过构造函数指定该锁是否是公平锁 默认是非公平锁 非公平锁的优点在于吞吐量比公平锁大.

  /**
     * Creates an instance of {@code ReentrantLock}.
     * This is equivalent to using {@code ReentrantLock(false)}.
     */
    public ReentrantLock() {
        sync = new NonfairSync();
    }

可重入锁又名递归锁,是指在同一个线程在外层方法获取锁的时候,再进入该线程的内层方法会自动获取锁(前提锁对象得是同一个对象或者class),不会因为之前已经获取过还没释放而阻塞。Java中ReentrantLock和synchronized都是可重入锁,可重入锁的一个优点是可一定程度避免死锁。

public class ReenterLockDemo {
    static class Phone {

        public synchronized void sendSms() {
            System.out.println(Thread.currentThread().getName() + "运行了sendSms方法");
            sendEmail();
        }

        public synchronized void sendEmail() {
            System.out.println(Thread.currentThread().getName() + "运行了sendEmail方法");
        }
    }

    public static void main(String[] args) {

        Phone phone = new Phone();
        new Thread(() -> {
            try {
                phone.sendSms();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }, "t1").start();

        new Thread(() -> {
            try {
                phone.sendSms();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }, "t2").start();
    }
}

执行结果

t1运行了sendSms方法
t1运行了sendEmail方法
t2运行了sendSms方法
t2运行了sendEmail方法

这说明了synchronized是一个可重入锁,例子中线程在执行sendMsm方法的时候就已经持有该对象的锁了,所以在进入内层的同步方法sendEmail时不必再次请求该对象锁,可以直接执行。

再看一下ReentrantLock,

public class ReenterLockDemo1 {

    public static void main(String[] args) {

        Phone phone = new Phone();
        Thread thread1 = new Thread(phone, "t1");
        thread1.start();
        Thread thread2 = new Thread(phone, "t2");
        thread2.start();
    }

    static class Phone implements Runnable {

        Lock lock = new ReentrantLock();

        @Override
        public void run() {
            get();
        }

        private void get() {
            lock.lock();
            try {
                System.out.println(Thread.currentThread().getName() + "执行了get方法");
                set();
            } finally {
                lock.unlock();
            }
        }

        private void set() {
            lock.lock();
            try {
                System.out.println(Thread.currentThread().getName() + "执行了set方法");
            } finally {
                lock.unlock();
            }
        }
    }
}

执行结果

t1执行了get方法
t1执行了set方法
t2执行了get方法
t2执行了set方法

可以说明ReentrantLock也是可重入锁。

自旋锁:自旋是一种“无锁式”的同步方法,CAS就是一种自旋的思想,它是指线程会采用循环的方式来获取锁,这个过程中线程不会阻塞,这也就避免了操作系统从用户态切换到内核态的切换,提高了性能。

public class SpinLock {

    //此时原子引用中的引用对象为空
    AtomicReference<Thread> atomicReference = new AtomicReference<>();

    public void mySpinLock() {
        //获取当前线程
        Thread thread = Thread.currentThread();
        while (!atomicReference.compareAndSet(null, thread)) {

        }
        System.out.println(thread.getName() + "获取了锁");
    }

    public void mySpinUnLock() {
        Thread thread = Thread.currentThread();
        atomicReference.compareAndSet(thread, null);
        System.out.println(thread.getName() + "释放了锁");
    }

    public static void main(String[] args) {

        SpinLock spinLock = new SpinLock();

        new Thread(() -> {
            spinLock.mySpinLock();
            try {
                TimeUnit.SECONDS.sleep(5);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            spinLock.mySpinUnLock();
        }, "t1").start();

        //将主线程暂停一秒保证是t1先获取到锁
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        new Thread(() -> {
            spinLock.mySpinLock();
            spinLock.mySpinUnLock();
        }, "t2").start();
    }
}

在上面的demo中,t1线程更改了主内存中的引用后暂停了5秒,这5秒里t2线程不断的执行CAS操作都不能成功,也就不断的循环,直到t1线程执行了Unlock方法将引用重新改为null,t2线程才可以跳出循环获取到锁。可以发现上面的方法中都没有synchronized,同样也能达到同步的效果。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值