本人编写这篇文章主要是为了记录本人的学习心得,理解上可能会有偏颇。尽量简短的去总结各个功能的特点。方便以后去回顾。也真心的希望本人的一些浅薄的见解能够得到大家的认可。
本人以下内容如果有错误和不足希望各位大神大佬们看见了后留言指出,本人将完善修改,尽量不去误解大家!!
Java多线程以及并发是作为一个Java开发程序猿是必须要掌握的一项知识点。之前也是一知半解,也是希望通过这篇文章把自己的理解经过记录下来。
首先,Java官方提供了对于多线程处理的juc包(全路径java.util.concurrent)。大家想要深入了解的话,可以去这个包下去仔细看下。
下面来说下CountDownLatch、CyclicBarrier、Semaphore这三个类:
一、作用:这三个类都可以实现在多线程情况下,控制并发数量。原理都是遵循AQS原理。
二、特点:
1、CountDownLatch 倒计数器,当计数器为0时,所有线程释放。子线程数量可以不等于设置的数量。计数器减小只需调用countDown()即可。不限制必须在子线程调用此方法。标红的字需要重点记忆一下,这是很重要的一个特点。
第一种情况:线程数和阈值是相同的,每个线程完成后调用countdown方法
private static void test3() {
//实例化个CountDownLatch 设置计数器阈值
CountDownLatch latch = new CountDownLatch(2);
Thread thread1 = new Thread(() -> {
try {
System.out.println("thread1 run start");
Thread.sleep(1000);
System.out.println("thread1 sleep 5000");
latch.countDown();
System.out.println("thread1 over");
} catch (InterruptedException e) {
e.printStackTrace();
}
});
Thread thread2 = new Thread(() -> {
try {
System.out.println("thread2 run start");
Thread.sleep(1000);
System.out.println("thread2 sleep 1000");
latch.countDown();
System.out.println("thread2 over");
} catch (InterruptedException e) {
e.printStackTrace();
}
});
thread1.start();
thread2.start();
try {
latch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("all thread over");
}
打印下:
thread1 run start
thread2 run start
thread1 sleep 5000
thread1 over
thread2 sleep 1000
thread2 over
all thread over
第二种情况:只有一个线程的情况下,在线程内两次调用countdown方法来释放
private static void test4() {
//实例化个CountDownLatch 设置计数器阈值
CountDownLatch latch = new CountDownLatch(2);
Thread thread1 = new Thread(() -> {
try {
System.out.println("thread1 run start");
System.out.println("thread1 sleep 500");
Thread.sleep(500);
System.out.println("thread1 over");
latch.countDown();
latch.countDown();
} catch (InterruptedException e) {
e.printStackTrace();
}
});
thread1.start();
try {
latch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("all thread over");
}
打印下结果
thread1 run start
thread1 sleep 500
thread1 over
all thread over
从打印结果看,这种情况也是没问题的。
第三种情况,countdown方法在主线程调用
private static void test5() {
//实例化个CountDownLatch 设置计数器阈值
CountDownLatch latch = new CountDownLatch(2);
Thread thread1 = new Thread(() -> {
try {
System.out.println("thread1 run start");
System.out.println("thread1 sleep 500");
Thread.sleep(500);
System.out.println("thread1 over");
latch.countDown();
} catch (InterruptedException e) {
e.printStackTrace();
}
});
thread1.start();
latch.countDown();
try {
latch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("all thread over");
}
打印下结果:
thread1 run start
thread1 sleep 500
thread1 over
all thread over
总结一下,CountDownLatch类只需要把定义好的阈值减少到0即可,调用countdown方法的数量等于阈值即可。而具体countdown方法是在哪里调用并没有严格的限制。
2、CyclicBarrier 特点:计数器,当计数器从0加到阈值后释放。
CyclicBarrier可以使一定数量的线程反复地在栅栏位置处汇集。当线程到达栅栏位置时将调用await方法,这个方法将阻塞直到所有线程都到达栅栏位置。如果所有线程都到达栅栏位置,那么栅栏将打开,此时所有的线程都将被释放,而栅栏将被重置以便下次使用。CyclicBarrier自身并不保证主线程在子线程完成之后执行。构造方法中的第二个参数就是最后一个到达的线程
应用场景:再不影响主线程运行的场景下,并且可重复
直接复制一下他人的
private static void test6() {
int threadCount = 3;
CyclicBarrier cyclicBarrier = new CyclicBarrier(threadCount,()->{
System.out.println("asdfasdf");
});
for (int i = 0; i < threadCount; i++) {
System.out.println("创建工作线程" + i);
Worker worker = new Worker(cyclicBarrier);
worker.start();
}
System.out.println("all thread over");
}
// 自定义工作线程
private static class Worker extends Thread {
private CyclicBarrier cyclicBarrier;
public Worker(CyclicBarrier cyclicBarrier) {
this.cyclicBarrier = cyclicBarrier;
}
@Override
public void run() {
super.run();
try {
System.out.println(Thread.currentThread().getName() + "开始等待其他线程");
cyclicBarrier.await();
System.out.println(Thread.currentThread().getName() + "开始执行");
// 工作线程开始处理,这里用Thread.sleep()来模拟业务处理
Thread.sleep(1000);
System.out.println(Thread.currentThread().getName() + "执行完毕");
} catch (Exception e) {
e.printStackTrace();
}
}
}
3、Semaphore 信号量为多线程协作提供更强大的控制。从广义上来讲,是锁的增强,synchronized、ReentrantLock只能对一个线程加锁,而信号量可以对多个线程进行控制。
应用场景:接口访问限流