了解JUC组件

避免线程在进入内核态的阻塞状态,是我们去分析和理解锁设计的关键

 

java.util.concurrent.locks.AbstractQueuedSynchronizer

简称AQS

底层 采用双向链表,是队列的一种实现,可以有多个Condition

设计:

1、使用Node实现FIFO队列,可以用于构建锁或者其他同步装置的基础框架

2、利用了一个int类型表示状态

3、使用方法是继承

4、子类通过继承并通过实现它的方法管理其他状态{acquire和release}的方法操纵状态

5、可以同时实现排它锁和共享锁模式(独占、共享)

原理:AQS内部维护了一个ClH队列来管理锁,首先线程会尝试获取锁,如果失败,就将当前线程以及等待状态等信息包成一个Node节点,加入到Sync同步队列,然后会不断尝试获取锁(如果失败就会阻塞自己,直到自己被唤醒),当持有锁的线程释放锁的时候会唤醒队列中的后继线程

 

AQS同步组件

1、CountDownLatch:闭锁,通过计数来保证是否需要阻塞

核心方法:countDown(),await() 一般组合使用,等countDown减到0,执行await()方法,await也可以设置时间,到了设定的值后就执行await()后面的内容

2、Semaphore(信号量):控制同一时间并发执行的数目

使用场景:仅能提供有限访问的资源:例:数据库连接数

,提供了acquire()和release() 方法

3、CyclicBarrier:功能和CountDownLatch类似

允许一组线程相互等待,直到到达某个屏障点,通过它可以完成多个线程相互等待,只有每个线程都准备就绪的时候,才能往下面执行

原理:线程调用了await()方法后,该线程就进入了等待状态,且计数器执行+1操作,当计数值达到了我们设定的初始值,调用了await() 的线程会被唤醒,继续执行,在释放等待后可以重用,所以又称为循环屏障

应用场景:可以用于多线程计算数据,最后合并计算结果

和CountDownLatch区别:

1、countDownLatch 只能使用一次 ,CyclicBarrier可以调用resret()方法重置,使用多次

2、countDownLatch主要实现一个或n个线程需要等待其他线程完成某项操作之后,才能继续往下执行,主要描述一个线程或n个线程等待其他线程的过程。而CyclicBarrier实现多个线程相互等待,直到所有线程都满足了条件之后才能继续执行后续的操作

new CyclicBarrier(5);

给定一个值,指需要有几个线程同步等待,每一个线程准备好 后,调用await()方法,当达到我们定义的值时,await() 后面的操作 才会执行,也可是设置awiat(2000,TimeUtil.MILLISECONDS),想要执行下面的内容,必须要捕捉他可能抛出的异常

 

**4、ReentrantLock(可重入锁):

1)ReentrantLock和synchronize的区别

1、可重入性:都是可重入锁,都是同一个线程进入一次锁的计数器自增一,所以等计数器下降为0时,才能释放锁

 

2、锁的实现

1)synchronize 基于JVM实现的(操作系统实现)

ReentrantLock时jdk实现的(用户敲代码实现)

3、性能的区别

1)在synchronize 性能优化以前,性能要比ReentrantLock差很多,但是自从synchronize引入了偏向锁,轻量级锁(自旋锁),俩者就差不多了,在俩种都可以使用的情况下 ,更推介synchronize,写法更容易,它的优化就是借鉴了ReentrantLock的CAS,都是在试图在用户态就把加锁问题解决避免进入内核态的线程阻塞。

4、功能的区别

1)synchronize使用更方便,并且是由编译器保证加锁和释放的,而ReetrantLock则需要手动加锁和释放锁的,为了避免手动释放锁造成死锁,所以在finalliy里中释放锁

2)细粒度和灵活度:很明显,ReentrantLock要优于synchronize

2)ReentrantLock独有的功能

1*、可指定是公平锁还是非公平锁

2*、提供了一个Condition类,可以分组唤醒需要唤醒的锁

3*、提供能够中断等待锁的线程机制,lock.lockInterruptibly()

是一种自旋锁,通过循环调用CAS操作来实现加锁,他的性能比较好,也是因为避免了使线程进入内核态的阻塞状态

 

synchronize的优点:除非需要对Lock有明确的需要,在使用synochronize 不可能释放锁,在退出synochronize块的时候JVM会帮助你释放锁,再用synochronize的时候,如果出现死锁,JVM能标示死锁,有利于找到问题,操作比较简单

 

ReentrantReadWriteLock,有读锁 和写锁,在有读或写的操作时 是不允许写或读的

如果想获得写入锁的时候,坚决不允许有读锁来保持的,如果读取情况很多的时候,会造成线程饥饿,写锁要一直等待读锁完成。(实际应用场景很少)属于悲观

StampedLock(由版本和模式组成)有三种模式;

乐观读:

StampedLock对吞吐量由巨大的改进,特别是在读线程越来越多的场景下

总结 锁的选取方式(参考):

1、当只有少量的竞争者的时候,synchronize是很好的通用锁实现

2、竞争者不少,线程增长的趋势是我们可以预估的,ReetrantLock是很好的通用锁实现

 

5、Condition(条件):

public static void main(String[] args) {

ReentrantLock reentrantLock = new ReentrantLock();

Condition condition = reentrantLock.newCondition();

 

new Thread(() -> {

try {

reentrantLock.lock();

log.info("wait signal"); // 1

condition.await();

} catch (InterruptedException e) {

e.printStackTrace();

}

log.info("get signal"); // 4

reentrantLock.unlock();

}).start();

 

new Thread(() -> {

reentrantLock.lock();

log.info("get lock"); // 2

try {

Thread.sleep(3000);

} catch (InterruptedException e) {

e.printStackTrace();

}

condition.signalAll();

log.info("send signal ~ "); // 3

reentrantLock.unlock();

}).start();

}

6、FutureTask:

 

实现线程的俩种常用方法继承Thread和实现Runnable,但是这俩种都无法获取线程的执行结果,从java1.5开始,提供了callable,Future,通过这俩个可以得到线程执行的结果

callable和Runable接口对比

runnable : public abstract void run();

callable : V call() throws Exception;

Future接口:可以监视目标线程调用call的情况,当调用get()方法后就可以获得它的结果,通常这个时候线程可能不会完成,当前线程就开始阻塞,直到call方法结束,返回结果,线程才会继续执行

总结:Future可以得到别的线程任务方法的返回值

FutureTask类:父类是RunnableFuture,RunnableFuture又继承了Runnable和Future俩个接口,所以他最终还是执行callable类型的任务,如果构造函数是Runnable类型的话,他会转换成callable类型,他既可以作为Runnable被线程执行,又可以作为Future得到callable的返回值

推介使用FutureTask

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

FutureTask<String> futureTask=new FutureTask<String>(new Callable<String>() {

@Override

public String call() throws Exception {

log.info("do something in callable");

Thread.sleep(5000);

return "OK";

}

});

 

new Thread(futureTask).start();

log.info("do something in main");

Thread.sleep(1000);

String result = futureTask.get();

log.info("result : {}",result);

}

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值