并发与多线程(二) ReentrantLock/ReadWriteLock/CountDownLatch/CyclicBarrier

        JUC(java.util.concurrent)包含了JAVA中并发和多线程相关的核心类.

ReentrantLock重入锁

        重入锁可以完全替代 synchronized 关键字。在JDK5.0的早期版本中,重入锁的性能远远好于synchronized,但从JDK6.0开始,JDK在synchronized上做了大量的优化,使得两者的性能差距并不大。但重入锁对逻辑控制的灵活性要远远好于synchronized。

        之所以称之为重入锁,就是一个线程允许反复进入,多次获得锁。当然,这里的反复仅仅局限于一个线程;如果同一个线程多次获锁,那么在释放锁的时候,也必须释放相同次数。如果释放锁的次数多于加锁,会字节返回异常java.lang.IllegalMonitorStateException,反之,如果释放锁的次数少,那么相当于线程还持有这个锁。

重入锁常用方法:

void lock():获得锁,如果锁已经被占用,则等待。

void lockInterruptibly():获得锁,但优先响应中断, 可以避免死锁。(与interrupt配套使用)

        使用 synchronized ,要么获得锁,要么保持等待。而重入锁,线程可以被中断的。也就是在等待锁的过程中,程序可以根据需要取消对锁的请求。即:如果一个线程正在等待锁,那么它依然可以收到一个通知,被告知无须再等待,可以停止工作了。可以很好的应对死锁问题

boolean tryLock():尝试获得锁,如果成功,返回true;如果失败则返回false;获得不到锁,则不进行等待,立即返回,可以避免死锁

boolean tryLock(long time, TimeUnit unit):在给定时间内尝试获得锁, 与tryLock()逻辑一致.可以避免死锁

boolean isHeldByCurrentThread():判断当前线程是否持有锁。

void unlock():释放锁。        

公平锁和非公平锁

        在默认的情况下,锁的申请是非公平锁。也就是获得锁的顺序不一定符合加锁的顺序。公平锁要求系统维护一个有序队列,实现成本比较高,性能相对也非常低下。因此,默认情况下,锁是非公平的。如果没有特别的需求,也不需要使用公平锁。

//默认情况或者指定入参指定是否公平
ReentrantLock lock1 = new ReentrantLock();//非公平锁
ReentrantLock lock2 = new ReentrantLock(false);//非公平锁
ReentrantLock lock3 = new ReentrantLock(true);//公平锁

Condition 重入锁的搭配类 

Condition常用方法

void await():会使当前线程等待,同时释放当前锁,当其他线程中使用signal()或者signalAll()方法时,线程会重新获得锁并继续执行。或者当线程被中断时,也能跳出等待。这和Object.wait()方法很相似。

void awaitUninterruptibly():与await()方法基本相同,区别是它不会在等待过程中响应中断(Uninterruptibly)。

long awaitNanos(long nanosTimeout):如果nanosTimeout时间内,没有被执行signal,则解除等待状态,该方法返回的long指的是——nanoTimeout值的估计值减去从该方法返回时等待的时间,小于或等于零表示没有剩余时间。

boolean await(long time, TimeUnit unit):指定时间内,没有被执行signal,解除等待状态。

boolean awaitUntil(Date deadline):到达deadline时间时,没有被执行signal,解除等待状态。

void signal():用于唤醒一个正在等待中的线程。

void signalAll():用于唤醒所有正在等待中的线程。

ReadWriteLock读写锁

        ReadWriteLock是JDK5中提供的读写分离锁。它允许多个线程同时读。但是考虑到数据的完整性,写写操作和读写操作间依然是需要相互等待和持有锁的。

如果在系统中,读操作的次数远远大于写操作,那么读写锁就可以发挥最大的效果,提升系统的性能。

CountDownLatch倒计时器

        CountDownLatch是一个多线程控制工具。用来控制线程的等待。设置需要countDown的数量num,然后每一个线程执行完毕后,调用countDown()方法,而主线程调用await()方法执行等待,直到num个子线程都执行了 countDown() 方法 ,则主线程开始继续执行。也就是主线程会等待所有子线程都执行完各自的任务之后主线程才会执行.

CountDownLatch countDownLatch = newCountDownLatch(2);
countDownLatch.await() ;//【主线程被阻塞】
countDownLatch.countDown() ;//【子线程A】
countDownLatch.countDown() ;//【子线程B】
//【主线程被激活,继续执行下面逻辑】
...

CyclicBarrier循环栅栏

        CyclicBarrier与CountDownLatch非常类似,它支持计数器的反复使用,CyclicBarrier可以理解为循环栅栏。CyclicBarrier可以接收一个参数作为Runnable barrierAction,每当计数器一次计数完成后——CyclicBarrier.await()时,系统会执行相应的的动作。

        CyclicBarrier.await()方法可能会抛出两种异常:一个是InterruptedException,也就是在等待过程中,线程被中断,应该说这是一个非常通用的异常,大部分迫使线程等待的方法都可能会抛出这个异常,使得线程在等待时依然可以响应外部紧急事件。另外一个异常则是CyclicBarrier特有的BrokenBarrierException,一旦遇到这个异常,则表示当前的CyclicBarrier已经破损了,可能系统已经没有办法等待所有线程到齐了,继续等待也没有意义.

AQS(AbstractQueuedSynchronizer)

源码解析——AQS 缪斯

 

         

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值