CountDownLatch
- 当前计数器的值为3;
- 线程a 调用了await()方法后,当前线程进入等待状态awaiting;
- 其他线程每次执行countDown()方法时,计数器就会减一,比如:线程1调用countDown()方法,计数器值为2,然后不停的执行,
- 只到计数器为0时,线程a 才继续执行。
如图:
我们可以看出CountDownLatch是java自带计数器,这个类可以阻塞线程,并保证线程满足某种特定的条件下再继续执行。保证线程执行完后,再进行其他的处理
Semaphore
可以阻塞线程,并且可以控制同一时间的请求的并发量,更适合控制同时进行的并发线程数
CountDownLatch和Semaphore 一般和线程池一起使用。
举例:
package com.mmall.concurrency.annoations;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* 课程里用来标记【线程不安全】的类或者写法
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.SOURCE)
public @interface NotThreadSafe {
String value() default "";
}
package com.mmall.concurrency.atomic;
import com.mmall.concurrency.annoations.ThreadSafe;
import lombok.extern.slf4j.Slf4j;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Semaphore;
import java.util.concurrent.atomic.AtomicInteger;
/**
* @Author: wn
* @Description: 线程不安全的类,反面案例
* 重点在于理解CountDownLath 和 Semaphore 作用和用法
* @Date: Created in 2019/1/11 11:45
*/
@Slf4j //日志
@ThreadSafe//自定义注解类
public class AtomicExample0 {
// 请求总数
public static int clientTotal = 5000;
// 同时并发执行的线程数
public static int threadTotal = 200;
public static int count = 0;
public static void main(String[] args) throws Exception {
ExecutorService executorService = Executors.newCachedThreadPool(); //创建线程池
final Semaphore semaphore = new Semaphore(threadTotal); //semaphore信号量,允许并发的数目
final CountDownLatch countDownLatch = new CountDownLatch(clientTotal);//闭锁,计数器,传入请求总数
for (int i = 0; i < clientTotal ; i++) {
executorService.execute(() -> {
try {
//信号量,acquire()时表示,当前线程数是否允许被执行,如果达到了一定并发数,这个add()方法将被阻塞
semaphore.acquire();
add();
semaphore.release();//信号量,执行完后,要释放当前线程release()
} catch (Exception e) {
log.error("exception", e);
}
countDownLatch.countDown();//没执行完一个线程,计数器减一
});
}
countDownLatch.await();//countDownLatch.await()方法保证当前计数器为0,也就是所有的线程已经执行完
executorService.shutdown();//关闭线程池
log.info("count:{}", count);//执行完所有线程后,打印count值
}
private static void add() {
count++;
}
}
运行结果:
5000
4989
4983
4805
运行结果不一定,不安全
重点理解CountDownLath 和 Semaphore 作用和用法
下面例子运行结果正确