Zhu.Yang

朱阳的个人博客(公众号:think123)

0%

CountDownLatch和CyclicBarrier区别

CountDownLatch 和 CyclicBarrier 是 Java 并发包提供的两个非常易用的线程同步工具类,把它们放在一起介绍是因为它们之间有点像,又很不同。

CountDownLatch 主要用来解决一个线程等待多个线程的场景,可以类比旅游团团长要等待所有的游客到齐才能去下一个景点。

CyclicBarrier 是一组线程之间互相等待,只要有一个线程没有完成,其他线程都要等待,更像是你和你老婆不离不弃。

对于CountDownLatch来说,重点是那一个线程, 是它在等待,而另外那N个线程在把“某个事情”做完之后可以继续等待,可以终止。

而对于CyclicBarrier来说,重点是那**一组(N个)**线程,他们之间任何一个没有完成,所有的线程都必须等待。

除此之外 CountDownLatch 的计数器是不能循环利用的,也就是说一旦计数器减到 0,再有线程调用 await(),该线程会直接通过。
但CyclicBarrier 的计数器是可以循环利用的,而且具备自动重置的功能,一旦计数器减到 0 会自动重置到你设置的初始值。除此之外,CyclicBarrier 还可以设置回调函数,可以说是功能丰富。

阅读全文 »

简介

ReentrantReadWriteLock支持读写锁,StampedLock支持写锁、悲观锁读和乐观读(无锁)。其中写锁、悲观读锁的语义和ReentrantReadWriteLock中的写锁、读锁语义一样,都是允许多个线程同时获取悲观锁读,但是只允许一个线程获取写锁,写锁和悲观读锁是互斥的。

不同的是:StampedLock 里的写锁和悲观读锁加锁成功之后,都会返回一个 stamp;然后解锁的时候,需要传入这个 stamp。

阅读全文 »

针对读多写少的场景,Java提供了另外一个实现Lock接口的读写锁ReentrantReadWriteLock(RRW),之前分析过ReentrantLock是一个独占锁,同一时间只允许一个线程访问。

而 RRW 允许多个读线程同时访问,但不允许写线程和读线程、写线程和写线程同时访问。
读写锁内部维护了两个锁,一个是用于读操作的ReadLock,一个是用于写操作的 WriteLock。

读写锁遵守以下三条基本原则

  1. 允许多个线程同时读共享变量;
  2. 只允许一个线程写共享变量;
  3. 如果一个写线程正在执行写操作,此时禁止读线程读共享变量。
阅读全文 »

之前有说过,我用traefik做网关,无论是内外网请求都会经过网关。

请求

但是我们有一部分API是只有内网会用,为了安全,我们要保证这些内网的API只有内网可以访问到。

但是由于之前的设置,这些API是匿名访问的,如果修改为需要权限,那么需要其他依赖于我们服务的team来做对应的修改,是由于一些原因,这条路走不通。

阅读全文 »

之前分析过AQS的源码,但只分析了独占锁的原理。

而刚好我们可以借助Semaphore来分析共享锁。

如何使用Semaphore

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
public class SemaphoreDemo {

public static void main(String[] args) {

// 申请共享锁数量
Semaphore sp = new Semaphore(3);

for(int i = 0; i < 5; i++) {
new Thread(() -> {
try {

// 获取共享锁
sp.acquire();

String threadName = Thread.currentThread().getName();

// 访问API
System.out.println(threadName + " 获取许可,访问API。剩余许可数 " + sp.availablePermits());

TimeUnit.SECONDS.sleep(1);

// 释放共享锁
sp.release();

System.out.println(threadName + " 释放许可,当前可用许可数为 " + sp.availablePermits());

} catch (InterruptedException e) {
e.printStackTrace();
}

}, "thread-" + (i+1)).start();
}
}
}
阅读全文 »

公平锁和非公平锁的区别?

之前分析AQS的时候,了解到AQS依赖于内部的两个FIFO队列来完成同步状态的管理,当线程获取锁失败的时候,会将当前线程以及等待状态等信息构造成Node对象并将其加入同步队列中,同时会阻塞当前线程。当释放锁的时候,会将首节点的next节点唤醒(head节点是虚拟节点),使其再次尝试获取锁。

同样的,如果线程因为某个条件不满足,而进行等待,则会将线程阻塞,同时将线程加入到等待队列中。当其他线程进行唤醒的时候,则会将等待队列中的线程出队加入到同步队列中使其再次获得执行权。

按照我们的分析,无论是同步队列还是等待队列都是FIFO,看起来就很公平呀?为什么ReentrankLock还分公平锁和不公平锁呢?

阅读全文 »

如何看待AQS?

看了Synchronized的实现方式之后,再来看JDK的AQS,感觉就比较简单了,它的行为有点像银行排队,银行有很多窗口处理很多业务,不同的窗口处理不同的业务,比如有个人业务,也有金融业务,也有公司业务等,每个窗口都有很多人排队。一般来讲当你在窗口处理业务的时候是不会有人来打扰你的,不一般的时候是什么时候呢?那就是资料分发窗口,谁来了都给你一张资料,一边看去吧。

而且当你去处理业务的时候,你可能有些资料忘记带了,然后你又要重新去取资料,取完之后回来继续排队。

所以说,代码源于生活,古人诚不欺我。

阅读全文 »

不知道你有没有想过,那些去申请锁的线程都怎样了?有些可能申请到了锁,马上就能执行业务代码。但是如果有一个锁被很多个线程需要,那么这些线程是如何被处理的呢?

今天我们走进synchronized 重量级锁,看看那些没有申请到锁的线程都怎样了。

ps: 如果你不想看分析结果,可以拉到最后,末尾有一张总结图,一图胜千言

阅读全文 »

项目开发中总是需要执行一些定时任务,比如定时处理数据之后发送邮件,定时更新缓存等等。

Java定时任务

  1. 基于 java.util.Timer 定时器,实现类似闹钟的定时任务
  2. 使用 Quartz、elastic-job、xxl-job 等开源第三方定时任务框架,适合分布式项目应用
  3. 使用 Spring 提供的一个注解: @Scheduled

项目框架使用的是SpringBoot,所以之前定时任务使用的是SpringBoot中的@Scheduled。可是这种方式并不适合我们现在的cloud环境,为了更加cloud native一点,我删除了使用SpringBoot写的37个定时任务,改为使用Kubernetes cronjob的方式。

阅读全文 »

模板解释器

我们都知道Java之所以可以一次编译到处运行,完全是因为字节码的原因,字节码就相当于中间层屏蔽了底层细节。但是想要在机器执行,最终还是要翻译成机器指令。

而JVM是通过C/C++来编写的,Java程序编译后,会产生很多字节码指令,每一个字节码指令在JVM底层执行的时候又会编程一堆C代码,这一堆C代码在编译之后又会编程很多的机器指令,这样我们的java代码到最终执行的机器指令那一层,所产生的机器指令时指数级的,这也就导致了Java执行效率低下。

早期的JVM是因为解释执行慢而被人诟病,那么有没有办法优化这个问题呢?我们发现之所以慢是因为java和机器指令之间隔了一层C/C++,而GCC之类的编译器又不能做到绝对的智能编译,所产生的机器码效率就不是很高。因此我们只要跳过C/C++这个层次,直接将Java字节码和本地机器码进行一个对应就可以了。

因此HotSpot的工程师们废弃了早期的解释执行器,而采用了模板执行器。所谓的模板就是将一个 java 字节码通过人工手动的方式编写为固定模式的机器指令,这部分不在需要 GCC 的帮助,这样就可以大大减少最终需要执行的机器指令,所以才能提高效率。

阅读全文 »