【P说】显示锁、ReentrantLock

Synchronized缺陷

  1. 当线程A拿到sync锁以后,线程B只能一直等待,知道线程A使用完并释放锁。
  2. 当我们进行读写操作时,读操作和读操作之间实际上是不会有冲突的,如果采用synchronized关键字实现同步的话,就会导致当多个线程都只是进行读操作时,也只有一个线程在可以进行读操作,其他线程只能等待锁的释放而无法进行读操作。
  3. 无法尝试的去获得锁。

Lock意义

可以说,Lock的出现就是为了解决synchronize的缺陷,实际上,synchronized在经过一段时间的优化之后,效率已经非常高了,如果没有出现以下情况,是不需要使用lock的,直接使用synchronized关键字就行。

  1. 获取锁可以被中断
  2. 超时获取锁
  3. 尝试获取锁
  4. 读多写少的读写锁

Lock是一个接口

public interface Lock {

	//请求锁
	void lock();

	//取锁可以被中断
    void lockInterruptibly() throws InterruptedException;

	//尝试获取锁
    boolean tryLock();

	//尝试获取锁(可设置超时时间)
    boolean tryLock(long time, TimeUnit unit) throws InterruptedException;

	//解锁
    void unlock();

	//获得Condition对象,该对象可以实现线程的阻塞与通知
	Condition newCondition();
}

ReentrantLock

ReentrantLock是Lock接口的代表性实现类,是一个可重入锁(锁可以被多次获得)。

使用

public class Lock {

    static java.util.concurrent.locks.Lock lock = new ReentrantLock();

    static class Thread1 extends Thread{
        @Override
        public void run() {
            System.out.println("线程A尝试获取锁...........");
            lock.lock();
            try {
                System.out.println("线程A获取到了锁...........");
                Thread.sleep(5000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                //在finally语句块中,保证锁一定会被释放
                lock.unlock();
                System.out.println("线程A释放了锁...........");
            }
        }
    }

    static class Thread2 extends Thread{
        @Override
        public void run() {
            System.out.println("线程B尝试获取锁...........");
            lock.lock();
            try {
                System.out.println("线程B获取到了锁...........");
                Thread.sleep(5000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                //在finally语句块中,保证锁一定会被释放
                lock.unlock();
                System.out.println("线程B释放了锁...........");
            }
        }
    }

    public static void main(String[] args) throws InterruptedException {
        Thread1 thread1 = new Thread1();
        Thread2 thread2 = new Thread2();
        thread1.start();
        thread2.start();

        thread1.join();
        thread2.join();
    }
}

结果:
在这里插入图片描述

公平锁和非公平锁

ReentrantLock 有两个构造函数,有一个可以传入一个布尔类型的值,true为生成一个公平锁,false表示生成一个非公平锁。

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

公平锁

每个线程都按照申请锁时候的队列顺序依次取得锁,第一个申请锁的人总是第一个获取锁。

非公平锁

多个线程去获取锁的时候,会直接去尝试获取,获取不到,再去进入等待队列,如果能获取到,就直接获取到锁。

优缺点

公平锁
优点:所有线程都可以被保证可以获取到锁。
缺点:吞吐量会下降,因为线程从挂起状态到被唤醒是需要消耗一定时间的,在这段时间里,公平锁无法进行任何操作。
非公平锁
优点:提高吞吐量,因为从线程挂起到唤醒这段时间,假设有其他线程已经可以获取锁就会直接获取,减少了等待时间。
缺点:可能导致队列中间的线程一直获取不到锁或者长时间获取不到锁,导致饿死。这种情况出现,是因为假设线程从挂起状态到被唤醒,发现已经有线程捷足先登了,就又会被挂起,之后又被唤醒,又发现被人捷足先登,就又挂起。。。一直在循环这个过程。

使用Condition对象实现线程通知

可以通过ReentrantLock 的newCondition()方法获得Condition对象,Condition对象的功能与wait(),notify(),notifyAll()一样。

public class Lock {

    static java.util.concurrent.locks.Lock lock = new ReentrantLock();
    static Condition condition = lock.newCondition();

    static class Thread1 extends Thread{
        @Override
        public void run() {
            System.out.println("线程A开始等待..........");
            lock.lock();
            try {
                condition.await();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }finally {
                lock.unlock();
            }
            System.out.println("线程A被唤醒..........");
        }
    }

    static class Thread2 extends Thread{
        @Override
        public void run() {
            System.out.println("线程B唤醒线程A..........");
            lock.lock();
            try {
                condition.signal();
            }finally {
                lock.unlock();
            }
        }
    }

    public static void main(String[] args) throws InterruptedException {
        Thread1 thread1 = new Thread1();
        Thread2 thread2 = new Thread2();

        thread1.start();
        //休眠五秒后用线程B唤醒线程A
        Thread.sleep(5000);
        thread2.start();
    }
}

结果:
在这里插入图片描述

ReetrantReadWriteLock

ReetrantReadWriteLock是一个读写锁,适用于读多写少的情景,运行多个线程同时获取读锁,但是只能有一个线程获取写锁,写锁被获取时,其他线程获取读锁或者写锁时都会被阻塞。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值