Java并发基础(6):ReentrantLock

目录

1、ReentrantLock

2、方法说明

3、Lock和TryLock的区别

4、thread.interrupt()方法能够中断线程B的等待过程

5、获取锁的方式源码解读

5.1、lock()

5.2、tryLock(long, TimeUnit) + 当线程.interrpt() ==》中断线程

5.3、lockInterruptibly() + 当前线程.interrupt() ==》中断线程

5.4、公平和非公平锁源码

1、ReentrantLock

  • synchronized重量级锁,可重入锁,互斥锁。JDK前期的版本lock比synchronized更快,在JDK1.5之后synchronized引入了偏向锁,轻量级锁和重量级锁。以致两种锁性能旗鼓相当。
  • ReentrantLock(轻量级锁)也可以叫对象锁,可重入锁,互斥锁。提供了fair和unfair两种模式的锁。默认构造函数是unfair的锁,如果初始化时传入true的参数则会返回fair锁。
    • 不公平:在锁可获取时,不用考虑该锁队列是否有其他waiter,直接获取;
    • 公平锁:按照排队顺序获取锁。

2、方法说明

  • lock():使用得最多的一个方法,用来获取锁。如果锁已被其他线程获取,则进行等待。
  • tryLock():用来尝试获取锁,如果获取成功,则返回true,如果获取失败(即锁已被其他线程获取),则返回false,无论如何都会立即返回。在拿不到锁时不会一直在那等待。
  • tryLock(long time, TimeUnit unit):在拿不到锁时会等待一定的时间,在时间期限之内如果还拿不到锁,就返回false。如果一开始拿到锁或者在等待期间内拿到了锁,则返回true,同时在等待的时候调用interrupt() 方法也会中断线程。
  • lockInterruptibly():当通过这个方法去获取锁时,如果线程正在等待获取锁,则这个线程能够响应中断,即中断线程的等待状态。也就使说,当两个线程同时通过lock.lockInterruptibly()想获取某个锁时,假若此时线程A获取到了锁,而线程B只有在等待,那么对线程B调用threadB.interrupt()方法能够中断线程B的等待过程。
  • lock.unlock():判断当前线程有没有获取到锁,如果没获取到锁就解锁会抛出 new IllegalMonitorStateException();

举一个例子如下:

        假如线程A和线程B使用同一个锁LOCK,此时线程A首先获取到锁LOCK.lock(),并且始终持有不释放。如果此时B要去获取锁,有四种方式:

  • LOCK.lock(): 此方式会始终处于等待中,即使调用B.interrupt()也不能中断,除非线程A调用LOCK.unlock()释放锁。

  • LOCK.lockInterruptibly(): 此方式会等待,但当调用B.interrupt()会被中断等待,并抛出InterruptedException异常,否则会与lock()一样始终处于等待中,直到线程A释放锁。

  • LOCK.tryLock(): 该处不会等待,获取不到锁并直接返回false,去执行下面的逻辑。

  • LOCK.tryLock(10, TimeUnit.SECONDS)该处会在10秒时间内处于等待中,但当调用B.interrupt()会被中断等待,并抛出InterruptedException。10秒时间内如果线程A释放锁,会获取到锁并返回true,否则10秒过后会获取不到锁并返回false,去执行下面的逻辑。

public void lock() {
    sync.lock();
}

public void lockInterruptibly() throws InterruptedException {
  sync.acquireInterruptibly(1);
}



public boolean tryLock() {
    return sync.nonfairTryAcquire(1);
}


public boolean tryLock(long timeout, TimeUnit unit)
        throws InterruptedException {
    return sync.tryAcquireNanos(1, unit.toNanos(timeout));
}

3、Lock和TryLock的区别

  • lock拿不到锁会一直等待。tryLock是去尝试,拿不到就返回false,拿到返回true。
  •  tryLock是可以被打断的,被中断 的,lock是不可以。

4、thread.interrupt()方法能够中断线程B的等待过程

示例:

        要执行doBussiness()中的代码首先需要得到锁lock,线程使用lockInterruptibly()方法获取锁。

如下示例中,t0得到了锁,t1等待。在t1等待时,调用t1.interrupt(),中断t1的等待。

public class LockTest {

        private Lock lock = new ReentrantLock();

        public void doBussiness() {
            String name = Thread.currentThread().getName();

            try {
                System.out.println(name + " 开始获取锁");
                lock.lockInterruptibly();
                System.out.println(name + " 得到锁");
                System.out.println(name + " 开工干活");
                for (int i=0; i<5; i++) {
                    Thread.sleep(1000);
                    System.out.println(name + " : " + i);
                }
            } catch (InterruptedException e) {
                System.out.println(name + " 被中断");
                System.out.println(name + " 做些别的事情");
            } finally {
                try {
                    lock.unlock();
                    System.out.println(name + " 释放锁");
                } catch (Exception e) {
                    System.out.println(name + " : 没有得到锁的线程运行结束");
                }
            }
        }

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

            LockTest lockTest = new LockTest();

            Thread t0 = new Thread(
                    new Runnable() {
                        public void run() {
                            lockTest.doBussiness();
                        }
                    }
            );

            Thread t1 = new Thread(
                    new Runnable() {
                        public void run() {
                            lockTest.doBussiness();
                        }
                    }
            );

            // 启动线程t1
            t0.start();
            Thread.sleep(10);
            // 启动线程t2
            t1.start();
            Thread.sleep(100);
            // 线程t1没有得到锁,中断t1的等待
            t1.interrupt();
        }

}

运行结果:

Thread-0 开始获取锁
Thread-0 得到锁
Thread-0 开工干活
Thread-1 开始获取锁
Thread-1 被中断
Thread-1 做些别的事情
Thread-1 : 没有得到锁的线程运行结束
Thread-0 : 0
Thread-0 : 1
Thread-0 : 2
Thread-0 : 3
Thread-0 : 4
Thread-0 释放锁

如果使用lock()方法获取锁,线程不会被中断。

lock.lockInterruptibly();

改为

lock.lock();

运行结果如下:

Thread-0 开始获取锁
Thread-0 得到锁
Thread-0 开工干活
Thread-1 开始获取锁
Thread-0 : 0
Thread-0 : 1
Thread-0 : 2
Thread-0 : 3
Thread-0 : 4
Thread-1 得到锁
Thread-1 开工干活
Thread-1 被中断
Thread-1 做些别的事情
Thread-1 释放锁
Thread-0 释放锁

去掉

t1.interrupt();

运行结果如下:

Thread-0 开始获取锁
Thread-0 得到锁
Thread-0 开工干活
Thread-1 开始获取锁
Thread-0 : 0
Thread-0 : 1
Thread-0 : 2
Thread-0 : 3
Thread-0 : 4
Thread-0 释放锁
Thread-1 得到锁
Thread-1 开工干活
Thread-1 : 0
Thread-1 : 1
Thread-1 : 2
Thread-1 : 3
Thread-1 : 4
Thread-1 释放锁

5、获取锁的方式源码解读

5.1、lock()

public void lock() {
        sync.lock();
    }

  • 当锁可用,并且当前线程没有持有该锁,直接获取锁并把count set为1。
  • 当锁可用,并且当前线程已经持有该锁,直接获取锁并把count增加1。
  • 当锁不可用,那么当前线程被阻塞,休眠一直到该锁可以获取,然后把持有count设置为1。

小结:该种方式获取锁不可中断,如果获取不到则一直休眠等待。

5.2、tryLock(long, TimeUnit) + 当线程.interrpt() ==》中断线程

5.3、lockInterruptibly() + 当前线程.interrupt() ==》中断线程

5.4、公平和非公平锁源码

//fair lock tryAcquire
if (c == 0) {
                if (!hasQueuedPredecessors() &&
                    compareAndSetState(0, acquires)) {
                    setExclusiveOwnerThread(current);
                    return true;
                }
            }
//unfair lock nonfairTryAcquire
if (c == 0) {
                if (compareAndSetState(0, acquires)) {
                    setExclusiveOwnerThread(current);
                    return true;
                }
            }
           

        公平锁fairLock在获取之前会看下自己是不是没有前继元素。而非公平锁unfairLock并不会。

参考:阿里面试实战题2----ReentrantLock里面lock和tryLock的区别 - wenbochang - 博客园

Lock的lockInterruptibly()方法

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值