part8-ReentrantLock学习

目录页:https://blog.****.net/u011294519/article/details/88367808

1. ReentrantLock

1.1.小声哔哔

    年轻的时候,说到锁想到的就是synchronized关键字,而且那时候项目要求不高,已经完全能满足需求,但是深入了解synchronized以后发现自己真的是年轻(貌似说synchronized已经被优化的与Lock差不多,但是真的了解过后发现由于synchronized本身的限制导致在读写锁方面是synchronized永远也赶不上的)。

    ReentrantLock从命名上来看就是可重入,但是synchronized其实也是可重入的

ReentrantLock与synchronized的区别:

  1. synchronized是依赖于JVM实现的,而ReenTrantLock是JDK实现的。
  2. 如果一个方法被Synchronized修饰作为对象锁,甚至是类锁(本质也是对象锁),那么一个线程获取到锁后其他线程就被阻塞,直到该线程释放锁。ReentrantLock支持设置等待时间,并且可阻断。
  3. 在释放锁的方式上,synchronized在开发过程中完全可以不用去关心,在方法执行完成或执行异常后,JVM会将锁释放,这也是内置关键字的好处。而ReentrantLock则需要我们显示的释放锁,而且必须是要写在finally中防止异常导致死锁。由此可以看出在使用方式上ReenTrantLock比synchronized更加灵活,但是synchronized使用起来更加简单。

1.2. ReentrantLock的一些特性

  1. ReenTrantLock可以通过构造方法指定使用公平锁还是非公平锁。Synchronized关键字只能是非公平锁。(PS:公平锁就是FIFO这种,但是非公平锁效率会更高,因为公平锁要加判断等进行实现,有这功夫活都干完了)
  2. ReenTrantLock利用AQS提供了一个Condition(条件)类,可以对线程分组唤醒,而synchronized则是会唤醒全部线程来竞争锁。
  3. ReenTrantLock可以使用tryLock尝试获取锁,并不是一直阻塞。

1.3.ReentrantLock主要方法

    构造方法ReentrantLock():初始化非公平锁

    构造方法public ReentrantLock(boolean fair):传入true初始化公平锁,若传入参数false则初始化非公平锁。

    lock():获取锁

    unlock():释放锁

    boolean tryLock():尝试获取锁,若获取成功则返回true

    boolean tryLock (long timeout, TimeUnit unit):尝试获取锁,若此时无法获取锁在等待timeout时间后放弃获取锁并返回false。

    lockInterruptibly():以响应中断的方式加锁

1.4. 上代码

1.4.1. 实现与synchronized相同效果

    先使用ReentrantLock达到和synchronized相同的效果,使用递归体现可重入

package com.concurrent.aqslock.part8.reent;

 

import java.util.concurrent.locks.ReentrantLock;

 

/**

 * 使用显示锁的范式

 */

public class LockDemo {

    private static ReentrantLock lock = new ReentrantLock();

 

    /**

     * 使用ReentrantLock锁

     */

    static class ReenTrantThread implements Runnable {

        private int count = 0;

 

        @Override

        public void run() {

            cyclic();

        }

 

        private void cyclic() {

            lock.lock();

            try {

                while (count < 2) {

                    System.out.println(Thread.currentThread().getName() + " count value: " + count);

                    Thread.sleep(1000);

                    ++count;

                    System.out.printf("Lock is get by CurrentThread: %s %s \n", Thread.currentThread().getName(), lock.isHeldByCurrentThread());

                    cyclic();

                    Thread.sleep(3000);

                }

            } catch (InterruptedException e) {

                e.printStackTrace();

            } finally {

                if (count >= 2 && lock.isHeldByCurrentThread()) {

                    System.out.printf("%s unlock ,queue length: %s \n",Thread.currentThread().getName(),lock.getQueueLength());

                    lock.unlock();

                }

            }

        }

    }

 

    /**

     * 使用synchronized锁

     */

    static class SysThread implements Runnable {

        private int count = 0;

 

        @Override

        public void run() {

            cyclic();

        }

 

        private synchronized void cyclic() {

            while (count < 2) {

                System.out.println(Thread.currentThread().getName() + " count value: " + count);

                ++count;

                cyclic();

            }

        }

    }

 

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

        for (int i = 0; i < 2; ++i) {

            new Thread(new ReenTrantThread(), "ReenTrantThread " + i).start();

        }

 

        for (int i = 0; i < 2; ++i) {

            new Thread(new SysThread(), "SysThread " + i).start();

        }

 

        Thread.sleep(3000);

    }

 

}

运行结果:

part8-ReentrantLock学习

代码位置:aqs-lock的part8

1.4.2. 使用可中断式获取锁

package com.concurrent.aqslock.part8.reent;

 

import java.util.concurrent.locks.ReentrantLock;

 

/**

 * 演示可中断式获取锁

 */

public class InterruptReenTrant {

    private static ReentrantLock lock = new ReentrantLock();

 

    public static void main(String[] arg0) {

 

        Thread t1 = new Thread(() -> {

            try {

                System.out.println(Thread.currentThread().getName() + " before get lock");

                lock.lockInterruptibly();

                Thread.sleep(3000);

                System.out.println(Thread.currentThread().getName() + " after get lock");

 

            } catch (InterruptedException e) {

                e.printStackTrace();

            } finally {

                if(lock.isHeldByCurrentThread()){

                    System.out.println(Thread.currentThread().getName()+" unlock");

                    lock.unlock();

                }

            }

        }, "first thread");

 

        Thread t2 = new Thread(() -> {

            try {

                System.out.println(Thread.currentThread().getName() + " before get lock");

                lock.lockInterruptibly();

                Thread.sleep(2000);

                System.out.println(Thread.currentThread().getName() + " after get lock");

 

            } catch (InterruptedException e) {

                e.printStackTrace();

            } finally {

                if(lock.isHeldByCurrentThread()){

                    System.out.println(Thread.currentThread().getName()+" unlock");

                    lock.unlock();

                }

            }

        }, "second thread");

 

        t1.start();

        t2.start();

        // 尝试注释和不注释下面代码

        t1.interrupt();

    }

 

}

运行结果:

part8-ReentrantLock学习

    代码位置:aqs-lock的part8

1.4.3. tryLock

package com.concurrent.aqslock.part8.reent;

 

import java.util.concurrent.TimeUnit;

import java.util.concurrent.locks.ReentrantLock;

 

/**

 * 演示tryLock

 */

public class TryLockReenTrant {

    private static ReentrantLock lock = new ReentrantLock();

 

    public static void main(String[] arg0) {

        Thread t1 = new Thread(() -> {

            try {

                System.out.println(Thread.currentThread().getName() + " before try lock");

                if(lock.tryLock()){

                    Thread.sleep(3000);

                    System.out.println(Thread.currentThread().getName() + " after try lock");

                }

 

            } catch (InterruptedException e) {

                e.printStackTrace();

            } finally {

                if(lock.isHeldByCurrentThread()){

                    System.out.println(Thread.currentThread().getName()+" unlock");

                    lock.unlock();

                }

            }

        }, "first thread");

 

        Thread t2 = new Thread(() -> {

            try {

                System.out.println(Thread.currentThread().getName() + " before try lock");

                if(lock.tryLock()){

                    Thread.sleep(2000);

                    System.out.println(Thread.currentThread().getName() + " after try lock");

                } else {

                    System.out.println(Thread.currentThread().getName() + " try lock fail");

                }

            } catch (InterruptedException e) {

                e.printStackTrace();

            } finally {

                if(lock.isHeldByCurrentThread()){

                    System.out.println(Thread.currentThread().getName()+" unlock");

                    lock.unlock();

                }

            }

        }, "second thread");

 

        Thread t3 = new Thread(() -> {

            try {

                System.out.println(Thread.currentThread().getName() + " before try lock");

                if(lock.tryLock(6L, TimeUnit.SECONDS)){

                    Thread.sleep(2000);

                    System.out.println(Thread.currentThread().getName() + " after try lock");

                } else {

                    System.out.println(Thread.currentThread().getName() + " try lock fail");

                }

            } catch (InterruptedException e) {

                e.printStackTrace();

            } finally {

                if(lock.isHeldByCurrentThread()){

                    System.out.println(Thread.currentThread().getName()+" unlock");

                    lock.unlock();

                }

            }

        }, "third thread");

 

        t1.start();

        t2.start();

        t3.start();

    }

}

运行结果:

part8-ReentrantLock学习

    代码位置:aqs-lock的part8

1.5. 看代码

    非公平队列的Lock():

part8-ReentrantLock学习

    可以看到,使用了我们的老朋友AQS的compareAndSetState方法,若state为0时则置为1,设置锁的拥有者为当前线程。

    若更新state失败,则进入acquire方法

part8-ReentrantLock学习

part8-ReentrantLock学习

    若获取到锁的为当前现在,则给state加1,并直接返回

    若获取到锁的不是当前线程,则进入acquireQueued方法

part8-ReentrantLock学习

    首先将当前线程加入等待队列,并挂起当前线程。