这个是在学习工作中的一些总结,若有不对之处欢迎大家指出。侵删!
需要源码联系QQ:1352057131
得之在俄顷,积之在平日。
目录
控制并发流程
控制并发流程工具类概览
CountDownLatch
作用
流程
主要方法
示例1
示例2
示例3
Semaphore
作用
使用流程
常用方法
示例
Condition接口(条件对象)
作用
常用方法
示例
示例2:消费者生产者模式
注意点
CyclicBarrier循环栅栏
作用
示例
CyclicBarrier与CountDownLatch的区别
控制并发流程就是让线程之间相互配合以满足业务需求,比如:线程1等待线程2 3 4执行完后再执行。
倒计时门闩 例如:等长途汽车,等到所有位置都有人了就发车
倒数结束之前,该线程一直等待,直到倒数结束之后,该线程才会执行
CountDownLatch(int count):构造函数,参数count为需要倒数的值
void await():调用该方法的线程会被挂起,直到count的值为0才会继续执行
void countDown():将count的值减1,直到为0时,等待的线程会被唤醒。
public class CountDownLatchTest01 {
//多等一:模拟五个检查官检查工作
private static CountDownLatch countDownLatch = new CountDownLatch(5);
public static void main(String[] args) {
//创建一个容量为5的线程池
ExecutorService executorService = Executors.newFixedThreadPool(5);
for (int i = 0; i < 5; i++) {
String name = "检查者"+(i+1);
Runnable runnable = new Runnable() {
@Override
public void run() {
System.out.println(name+"开始检查");
try {
Thread.sleep(new Random().nextInt(1000)*10);
System.out.println(name+"检查结束");
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
countDownLatch.countDown();
}
}
};
executorService.submit(runnable);
}
try {
countDownLatch.await();
while (true){
if (!executorService.isTerminated()){
executorService.shutdown();
break;
}
}
System.out.println("所有人检查结束");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public class CountDownLatchTest02 {
//一等多:模拟比赛发令枪
private static CountDownLatch countDownLatch = new CountDownLatch(1);
public static void main(String[] args) throws InterruptedException {
//创建一个容量为5的线程池
ExecutorService executorService = Executors.newFixedThreadPool(5);
for (int i = 0; i < 5; i++) {
String name = "选手"+(i+1);
Runnable runnable = new Runnable() {
@Override
public void run() {
System.out.println(name+"等待");
try {
countDownLatch.await();
System.out.println(name+"开始跑步");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
executorService.submit(runnable);
}
//等待所有的线程都准备完毕
Thread.sleep(1000);
System.out.println("发令枪响.....");
countDownLatch.countDown();
while (true){
if (!executorService.isTerminated()){
executorService.shutdown();
break;
}
}
}
}
public class CountDownLatchTest03 {
//一等多、多等一的混合使用
private static CountDownLatch begin = new CountDownLatch(1);
private static CountDownLatch end = new CountDownLatch(5);
public static void main(String[] args) throws InterruptedException {
//创建一个容量为5的线程池
ExecutorService executorService = Executors.newFixedThreadPool(5);
for (int i = 0; i < 5; i++) {
String name = "选手"+(i+1);
Runnable runnable = new Runnable() {
@Override
public void run() {
System.out.println(name+"等待");
try {
begin.await();
System.out.println(name+"开始跑步");
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
end.countDown();
}
}
};
executorService.submit(runnable);
}
//等待所有的线程都准备完毕
Thread.sleep(1000);
System.out.println("发令枪响.....");
begin.countDown();
end.await();
while (true){
if (!executorService.isTerminated()){
executorService.shutdown();
break;
}
}
System.out.println("所有人跑步完毕");
}
}
【CountDownLatch是不能重用的,如果需要重新计数则考虑CyclicBarrier或者创建新的CountDownLatch】
限制有限资源的使用;例如:一个服务的方法特别耗时(大量数据的处理、大量文件生成等),如果大量用户请求过来,则导致该服务不可用,如果使用信号量,前面的请求则会拿到许可证,后面的请求将会被阻塞,这样就保障了该服务不会同时服务特别多的用户而导致服务不可用。
初始化semaphore并指定许可证数量。
执行任务之前调用acquire()方法或者acquireUninterRuptibly()方法。
在任务结束后调用release()方法。
Semaphore(int permits):实例化Semaphore并指定令牌数量为permits。
Semaphore(int permits, boolean fair) :实例化Semaphore并指定令牌数量和指定是否公平;如果fair为true,将会把等待的线程放入FIFO队列里面
void acquire():获取一个令牌,可以响应中断。
void acquireUninterruptibly():获取一个令牌,不可以响应中断
boolean tryAcquire():看看有没有许可证,如果没有就去做别的事儿,过一会再来拿。
boolean tryAcquire(long timeout, TimeUnit unit):与tryAcquire一样,只是设置了一个超时时间
void release():释放一个令牌。
void acquire(int permits):获取permits个令牌。
boolean tryAcquire(int permits):看看有没有permits个许可证,如果没有就去做别的事儿,过一会再来拿。
boolean tryAcquire(int permits, long timeout, TimeUnit unit):与tryAcquire(int permits)一样,只是设置了一个超时时间。
void release(int permits):释放permits个令牌。
【每次获得了令牌必须在finaly中释放】
public class SemaphoreTest {
//创建拥有4个许可证的信号量
static Semaphore semaphore = new Semaphore(4);
public static void main(String[] args) {
ExecutorService service = Executors.newFixedThreadPool(10);
for (int i = 0; i < 10; i++) {
service.submit(new Task());
}
}
}
class Task implements Runnable{
@Override
public void run() {
try {
//拿到许可证
semaphore.acquire();
System.out.println(Thread.currentThread().getName()+"拿到许可证");
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
System.out.println(Thread.currentThread().getName()+"释放许可证");
semaphore.release();
}
}
}
当线程1需要等待某个条件时,它就会执行condition.await()方法,线程进入阻塞状态;假如线程2去执行对应的条件,当这个条件达成时就去执行condition.signal()方法,这时JVM就会从阻塞中的线程中找到等待该condition的线程,这时线程1就会收到可执行信号,线程状态就会变成Runable
void await():等待,线程进入阻塞状态
void signal():唤起一个正在等待的线程
void signalAll():唤起所有正在等待的线程
public class ConditionTest {
private static ReentrantLock lock = new ReentrantLock();
private static Condition condition = lock.newCondition();
public static void main(String[] args) {
new Thread(new Runnable() {
@Override
public void run() {
try {
lock.lock();
Thread.sleep(1000);
condition.signal();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
try {
lock.lock();
System.out.println("----------");
condition.await();
System.out.println("==========");
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}).start();
}
}
public class ConditionTest02 {
static ReentrantLock lock = new ReentrantLock();
static Condition consumerCondition = lock.newCondition();
static Condition producerCondition = lock.newCondition();
static ArrayBlockingQueue
public static void main(String[] args) {
Thread thread0 = new Thread(new Consumer());
Thread thread1 = new Thread(new Producer());
thread0.start();
thread1.start();
}
static class Consumer implements Runnable{//消费者
@Override
public void run() {
while (true){
Cons();
}
}
private void Cons(){
try {
lock.lock();
while (queue.size()==0){
System.out.println("队列里没有数据,等待补充数据");
consumerCondition.await();
producerCondition.signalAll();
}
System.out.println("队列里有"+queue.size()+"条数据消费了1条数据还剩"+(queue.size()-1)+"条数据");
queue.poll();
producerCondition.signalAll();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
static class Producer implements Runnable{//生产者
@Override
public void run() {
while (true){
Prod();
}
}
private void Prod(){
try {
lock.lock();
int i = 0;
while (queue.size()==10){
System.out.println("队列里已满");
producerCondition.await();
consumerCondition.signalAll();
}
System.out.println("队列里有"+queue.size()+"条数据补充了1条数据还剩"+(queue.size()+1)+"条数据");
queue.add("数据"+(i++));
consumerCondition.signalAll();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
}
Condition的用法与Object.wait/notify的用法几乎一样。
Await方法自动释放持有的lock锁,和Object.wait一样不需要手动释放锁。
调用await方法的时候必须持有锁,不然会抛出异常。
CyclicBarrier循环栅栏和CountDownLatch很类似,都能阻塞一组线程。
当有大量线程相互配合分别计算不同的任务,并且最后需要统一汇总的时候,我们可以使用CyclicBarrier;CyclicBarrier可以构造一个集结点,当某一个线程执行完毕,它就会到集结点等待,直到所有线程都到达集结点,那么该栅栏就被撤销,所有的线程再统一出发执行剩余的任务。
public class CyclicbarrierTest {
private static Vector
public static void main(String[] args) {
//创建容量为5的CyclicBarrier
CyclicBarrier cyclicBarrier = new CyclicBarrier(5, new Runnable() {
@Override
public void run() {
System.out.println("所有线程处理完毕");
System.out.println(vector.toString());
}
});
ExecutorService executorService = Executors.newFixedThreadPool(5);
for (int i = 0; i < 5; i++) {
executorService.submit(new Task(vector,cyclicBarrier));
}
//while (executorService.isTerminated()){}
executorService.shutdown();
}
static class Task implements Runnable{
private Vector
private CyclicBarrier cyclicBarrier;
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"开始处理数据");
try {
Thread.sleep(new Random().nextInt(10)*1000);
vector.add(Thread.currentThread().getName());
System.out.println(Thread.currentThread().getName()+"数据处理完毕");
cyclicBarrier.await();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
}
public Vector
return vector;
}
public void setVector(Vector
this.vector = vector;
}
public CyclicBarrier getCyclicBarrier() {
return cyclicBarrier;
}
public void setCyclicBarrier(CyclicBarrier cyclicBarrier) {
this.cyclicBarrier = cyclicBarrier;
}
public Task(Vector
this.vector = vector;
this.cyclicBarrier = cyclicBarrier;
}
}
}
【如果有5个以上的线程,也会进行5个一处理】
作用不同:CyclicBarrier要等固定数量的线程都到达了栅栏位置才能继续执行,而CountDownLatch只需要等到数字为0,也就是CyclicBarrier作用于线程CountDownLatch作用于方法。
可重用性:CountDownLatch不可重复使用,而CyclicBarrier可以重复使用。