java锁之公平,非公平锁,可重入锁(递归锁)

    什么是java的公平锁呢,就是很公平,在并发环境中,每个线程在获取锁时会先看此锁维护的等待队列,如果为空,或者当前线程是等待队列的第一个就占有锁,否则就会加入到等待队列中,以后会按照FIFO的规则从队列中取到自己,多个线程按照申请锁的顺序来获取锁,类似排队打饭,先来后到,非公平锁呢,是指多个线程获取锁的顺序并不是按照申请锁的顺序,有可能后申请的线程比先申请的线程优先获取锁,在高并发的情况下,有可能会造成优先级反转或者饥饿现象,非公并锁比较粗鲁,上来就直接尝试占有锁,如果并发包中ReentrantLock的创建可以指定构造函数的boolean类型来得到公平锁或非公平锁,默认是非公平锁,看一下ReentrantLock的构造函数,默认的构造函数是创建非公平锁,构造函数中指定true的时候是创建公平锁。

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

    /**
     * Creates an instance of {@code ReentrantLock} with the
     * given fairness policy.
     *
     * @param fair {@code true} if this lock should use a fair ordering policy
     */
    public ReentrantLock(boolean fair) {
        sync = fair ? new FairSync() : new NonfairSync();
    }

    那可重入锁呢,也称为递归锁,指的是同一线程外层函数获得锁之后,内层递归函数仍然能获取该锁的代码,在同一个线程在外层方法获取锁的时候,在进入内层方法会自动获取锁,也就是说,线程可以进入任何一个它已经拥有的锁所同步着的代码块。概念是抽象的,我们来看一段代码吧,要做的事情呢,就是一个资源类Phone中有两个加锁的同步方法即synchronized 修饰的sendSMS和sendEmail方法,其中sendSMS方法中调用sendEmail方法,然后再main方法中,启动两个线程t1和t2来调用sendEmail方法,看看执行结果。

public class ReentrantLockDemo {

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

    }

}

class Phone {

    public synchronized void sendSMS() throws Exception {
        System.err.println(Thread.currentThread().getName() + "\t invoked sendSMS()");
        sendEmail();
    }

    public synchronized void sendEmail() throws Exception {
        System.err.println(Thread.currentThread().getName() + "\t invoked sendEmail()");
    }
}

    从执行结果来看,一个线程执行完成后,另一个线程才能执行,这就说明,当一个线程获取到外层的方法锁的同时,它会自动获取内层方法的锁,也说明synchronized是可重入锁。

t1     invoked sendSMS()
t1     invoked sendEmail()
t2     invoked sendSMS()
t2     invoked sendEmail()

    synchronized是可重入锁,那ReentrantLock呢,我们继续再来看一段代码,也是资源类Phone中,有一个get和set方法,这里不同的是,get和set方法不是synchronized来修饰,而是ReentrantLock类来加锁,get方法中调用set方法,还是在启动t3和t4两个线程,调用get方法,来看看执行结果。

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class ReentrantLockDemo {

    public static void main(String[] args) throws Exception {

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

        Thread.sleep(1000);
        Thread t3 = new Thread(phone, "t3");
        Thread t4 = new Thread(phone, "t4");

        t3.start();
        t4.start();
    }

}

class Phone implements Runnable {

    public synchronized void sendSMS() throws Exception {
        System.err.println(Thread.currentThread().getName() + "\t invoked sendSMS()");
        sendEmail();
    }

    public synchronized void sendEmail() throws Exception {
        System.err.println(Thread.currentThread().getName() + "\t invoked sendEmail()");
    }

    Lock lock = new ReentrantLock();

    @Override
    public void run() {

        get();
    }

    private void get() {

        lock.lock();
        try {
            System.err.println(Thread.currentThread().getName() + "\t invoked get()");
            set();
        } finally {
            lock.unlock();
        }
    }

    private void set() {

        lock.lock();
        try {

            System.err.println(Thread.currentThread().getName() + "\t invoked set()");
        } finally {
            lock.unlock();
        }
    }
}

    从执行结果来看,和synchronized是一样的效果,当一个线程拿到外层方法的锁后,也同时拿到内层方法的锁,说明ReentrantLock也是可重入锁

t1     invoked sendSMS()
t1     invoked sendEmail()
t2     invoked sendSMS()
t2     invoked sendEmail()
t3     invoked get()
t3     invoked set()
t4     invoked get()
t4     invoked set()

    我们再来回顾下可重入锁的概念,也称为递归锁,指的是同一线程外层函数获得锁之后,内层递归函数仍然能获取该锁的代码,在同一个线程在外层方法获取锁的时候,在进入内层方法会自动获取锁,也就是说,线程可以进入任何一个它已经拥有的锁所同步着的代码块,下篇见。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值