前言
一文读懂java多线程下常用常考的阻塞方法LongAdder、CountDownLatch、CyclicBarrier、Phaser
包含演示代码
高并发模拟,性能比较实例代码
前言
LongAdder
synchronized VS Atomic Vs LongAdder
CountDownLatch
CyclicBarrier
Phaser
LongAdder
在高并发下LongAdder是如何工作的?
LongAdder采用了分段锁的概念,在高并发下,如果是连续递增,这LongAdder会分成几块进行递增然后再相加,从而提高效率
synchronized VS Atomic Vs LongAdder
在高并发下 synchronized VS Atomic Vs LongAdder谁的效率更高?
这是不一定的,需要看具体的应用场景和数据量
synchronized 是通过加锁保证了线程同步,相对来说效率要低一些
Atomic 类中都是原子操作,可以称之为无锁优化,底层通过unsafe实现,不能完全保证线程安全性
unsafe 是java很底层的一个类,里面大多直接调用了native(本地)方法实现。
我们不能直接看到,unsafe可以直接操作指针,内存。之前版本还可以调用出来使用,现在应该是不可以使用了
LongAdder 内部采用了分段锁的概念,就是在高并发自增的情况下,可以分出来几块自增,然后进行相加,从而提高效率
下面代码模拟1000线程,没有线程操作100000次,
synchronized VS Atomic Vs LongAdder 那个效率更高
package 线程同步LongAdder;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.atomic.LongAdder;
/**
* @program: solution
* @description: LongAdder内部类似与分段锁,如果发现多个相同(比如递增的操作)
* 它会分几块递增然后相加操作。
* 所以在并发量很高的情况下,效率高于synchronized和atomic
*在一千个线程每个线程操作十万次的情况下,效率对比
* synchronized VS Atomic Vs LongAdder
* @author: Wang Hai Xin
* @create: 2022-11-08 10:17
**/
public class testLongAdder {
static Long count1 = 0L;
static AtomicLong count2 = new AtomicLong(0);
static LongAdder count3 = new LongAdder();
public static void main(String[] args) throws InterruptedException {
Thread[] threads = new Thread[1000];
/*synchronized*/
Object lock = new Object();
for (int i = 0; i < threads.length; i++) {
threads[i] = new Thread(()->{
for (int j = 0; j < 100000; j++)
synchronized (lock){
count1++;
}
});
}
long start = System.currentTimeMillis();
for (Thread thread : threads) {
thread.start();
}
for (Thread thread : threads) {
try {
thread.join();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
long end = System.currentTimeMillis();
System.out.println("syn: " + count1 + " time: " + (end - start));
/*Atomic*/
start = System.currentTimeMillis();
for (int i = 0; i < threads.length; i++) {
threads[i] = new Thread(()->{
for (int j = 0; j < 100000; j++) {
count2.incrementAndGet();
}
});
}
for (Thread thread : threads) {
thread.start();
}
for (Thread thread : threads) {
thread.join();
}
end = System.currentTimeMillis();
System.out.println("Atomic: " + count2.get() + " time: " + (end - start));
/*LongAdder*/
start = System.currentTimeMillis();
for (int i = 0; i < threads.length; i++) {
threads[i] = new Thread(()->{
for (int i1 = 0; i1 < 100000; i1++) {
count3.increment();
}
});
}
for (Thread thread : threads) {
thread.start();
}
for (Thread thread : threads) {
thread.join();
}
end = System.currentTimeMillis();
System.out.println("LongAdder: " + count3.longValue() + " time: " + (end - start));
}
}
运行结果
syn: 100000000 time: 4198
Atomic: 100000000 time: 2322
LongAdder: 100000000 time: 259
注意并不是说LongAdder一定比其它两个效率高,只能是在特定条件下。
CountDownLatch
CountDownLatch 作用和jion()方法类似,像一个门栓,调用CountDownLatch.countDown()就会减一
当减少到0之后线程会继续运行。是一个阻塞的方法。
CountDownLatch 主要用在一个线程等待多个线程的情况下使用
代码如下
package ReentrantLock和LongAdder;
import java.util.concurrent.CountDownLatch;
/**
- @program: solution
- @description: CountDownLatch 使用演示
- @author: Wang Hai Xin
-
@create: 2022-11-10 15:29
**/
public class CountDownT {
public static void main(String[] args) {
CountDownT countDownT = new CountDownT();
countDownT.usingCountDowm();
countDownT.usingJion();
}
public void usingCountDowm(){
Thread[] threads = new Thread[100];
CountDownLatch countDownLatch = new CountDownLatch(threads.length);
for (int i = 0; i < threads.length; i++) {
threads[i] = new Thread(()->{
int result = 0;
for (int i1 = 0; i1 < 10000; i1++) {
result++;
}
countDownLatch.countDown();
});
}
for (int i = 0; i < threads.length; i++) {
threads[i].start();
}
try {
countDownLatch.await();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println("end latch");
}
public void usingJion(){
Thread[] threads = new Thread[100];
CountDownLatch countDownLatch = new CountDownLatch(threads.length);
for (int i = 0; i < threads.length; i++) {
threads[i] = new Thread(()->{
int result = 0 ;
for (int i1 = 0; i1 < 10000; i1++) {
result++;
}
countDownLatch.countDown();
});
}
for (int i = 0; i < threads.length; i++) {
threads[i].start();
}
for (int i = 0; i < threads.length; i++) {
try {
threads[i].join();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
System.out.println("end join");
}
}
>运行结果
```java
end latch
end join
CyclicBarrier
CyclicBarrier 也是一个阻塞的方法,不过与countDownLatch不同,CyclicBarrier类似与一个起跑线。当数量够了之后,一起起跑。
CyclicBarrier 用在多个线程相互等待的场景
如下代码 ,我们在创建的时候规定,满20个线程,执行程序。不满20个 就会阻塞在这里
package ReentrantLock和LongAdder;
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
/**
* @program: solution
* @description: CycliBarrier 用法演示
* @author: Wang Hai Xin
* @create: 2022-11-10 15:48
**/
public class CyclicBarrierT {
public static void main(String[] args) {
CyclicBarrier cyclicBarrier = new CyclicBarrier(20, new Runnable() {
@Override
public void run() {
System.out.println("满20人 ,发车");
}
});
for (int i = 0; i < 100; i++) {
new Thread(()->{
try {
cyclicBarrier.await();
} catch (InterruptedException e) {
throw new RuntimeException(e);
} catch (BrokenBarrierException e) {
throw new RuntimeException(e);
}
}).start();
}
}
}
Phaser
phaser 类似与周期性的CyclicBarrier, 可以通过构造函数或者使用 phaser.bulkRegister(7);注册
现在我们模拟一个结婚的场景,必须所有人到场才能吃饭,所有人离席 新郎新娘才能抱抱
代码演示如下:
package ReentrantLock和LongAdder;
import 线程同步volatile单例.T;
import java.util.concurrent.Phaser;
import java.util.concurrent.TimeUnit;
/**
* @program: solution
* @description: Phaser 演示代码,Phaser用于分段阻塞
* @author: Wang Hai Xin
* @create: 2022-11-11 09:49
**/
public class PhaserT {
static PhaserDemo phaser = new PhaserDemo();
public static void main(String[] args) {
phaser.bulkRegister(7);
for (int i = 0; i < 5; i++) {
final int name = i;
new Thread(
new Person("p"+i)
).start();
}
new Thread(
new Person("新郎")
).start();
new Thread(
new Person("新娘")
).start();
}
static class PhaserDemo extends Phaser {
/*参数 phase 表示第几个周期,registeredParties表示这个周期注册的线程数*/
@Override
protected boolean onAdvance(int phase, int registeredParties) {
switch (phase){
case 0 :{
System.out.println("所有人到场"+registeredParties);
return false;
}
case 1:{
System.out.println("所有人吃完"+registeredParties);
return false;
}
case 2 :{
System.out.println("所有人离场"+registeredParties);
return false;
}
case 3 : {
System.out.println("新郎新娘抱抱"+registeredParties);
return true;
}
default:{
return true;
}
}
}
}
static class Person implements Runnable {
public String name = null;
public Person(String s) {
name = s;
}
/*睡眠方法*/
void milliSleep(){
try {
TimeUnit.MILLISECONDS.sleep(100);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
public void arrive(){
milliSleep();
System.out.printf( "%s 到达现场! \n",name);
System.out.println();
/*注册Phaser*/
phaser.arriveAndAwaitAdvance();
}
public void eat(){
milliSleep();
System.out.printf("%s 开始吃饭",name);
System.out.println();
phaser.arriveAndAwaitAdvance();
}
public void leave(){
milliSleep();
System.out.printf("%s 离开",name);
System.out.println();
phaser.arriveAndAwaitAdvance();
}
public void hug(){
milliSleep();
if ("新郎".equals(name) || "新娘".equals(name)) {
System.out.printf("%s 洞房",name);
System.out.println();
phaser.arriveAndAwaitAdvance();
}else{
/*不是新郎新娘不可以进入洞房,这里注销掉,注销掉仍然算是一个线程到达*/
phaser.arriveAndDeregister();
}
}
@Override
public void run() {
arrive();
eat();
leave();
hug();
}
}
}
运行结果
p1 到达现场!
p3 到达现场!
p2 到达现场!
p4 到达现场!
新郎 到达现场!
新娘 到达现场!
p0 到达现场!
所有人到场7
p2 开始吃饭
p1 开始吃饭
新郎 开始吃饭
p3 开始吃饭
p0 开始吃饭
p4 开始吃饭
新娘 开始吃饭
所有人吃完7
p1 离开
p2 离开
p3 离开
p4 离开
新娘 离开
p0 离开
新郎 离开
所有人离场7
新郎 洞房新娘 洞房
新郎新娘抱抱2
进程已结束,退出代码0