JUC只线程之间协作

这个是在学习工作中的一些总结,若有不对之处欢迎大家指出。侵删!
需要源码联系QQ:1352057131
得之在俄顷,积之在平日。

 

目录

控制并发流程

控制并发流程工具类概览

CountDownLatch

作用

流程

主要方法

示例1

示例2

示例3

Semaphore

作用

使用流程

常用方法

示例

Condition接口(条件对象)

作用

常用方法

示例

示例2:消费者生产者模式

注意点

CyclicBarrier循环栅栏

作用

示例

CyclicBarrier与CountDownLatch的区别


控制并发流程

控制并发流程就是让线程之间相互配合以满足业务需求,比如:线程1等待线程2 3 4执行完后再执行。

控制并发流程工具类概览

 

CountDownLatch

作用

倒计时门闩 例如:等长途汽车,等到所有位置都有人了就发车

流程

倒数结束之前,该线程一直等待,直到倒数结束之后,该线程才会执行

主要方法

CountDownLatch(int count):构造函数,参数count为需要倒数的值

void await():调用该方法的线程会被挂起,直到count的值为0才会继续执行

void countDown():将count的值减1,直到为0时,等待的线程会被唤醒。

示例1

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();
        }
    }
}

示例2

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;
            }
        }
    }
}

示例3

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

作用

限制有限资源的使用;例如:一个服务的方法特别耗时(大量数据的处理、大量文件生成等),如果大量用户请求过来,则导致该服务不可用,如果使用信号量,前面的请求则会拿到许可证,后面的请求将会被阻塞,这样就保障了该服务不会同时服务特别多的用户而导致服务不可用。

使用流程

初始化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();
        }
    }
}

Condition接口(条件对象)

作用

当线程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();
    }
}

示例2:消费者生产者模式

public class ConditionTest02 {
    static ReentrantLock lock = new ReentrantLock();
    static Condition consumerCondition = lock.newCondition();
    static Condition producerCondition = lock.newCondition();
    static ArrayBlockingQueue<String> queue = new ArrayBlockingQueue<>(10);
    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循环栅栏

作用

CyclicBarrier循环栅栏和CountDownLatch很类似,都能阻塞一组线程。

当有大量线程相互配合分别计算不同的任务,并且最后需要统一汇总的时候,我们可以使用CyclicBarrier;CyclicBarrier可以构造一个集结点,当某一个线程执行完毕,它就会到集结点等待,直到所有线程都到达集结点,那么该栅栏就被撤销,所有的线程再统一出发执行剩余的任务。

示例

public class CyclicbarrierTest {
    private static Vector<String> vector = new Vector<String>();
    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<String> vector = new Vector<String>();
        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<String> getVector() {
            return vector;
        }
        public void setVector(Vector<String> vector) {
            this.vector = vector;
        }
        public CyclicBarrier getCyclicBarrier() {
            return cyclicBarrier;
        }
        public void setCyclicBarrier(CyclicBarrier cyclicBarrier) {
            this.cyclicBarrier = cyclicBarrier;
        }
        public Task(Vector<String> vector, CyclicBarrier cyclicBarrier) {
            this.vector = vector;
            this.cyclicBarrier = cyclicBarrier;
        }
    }
}

【如果有5个以上的线程,也会进行5个一处理】

CyclicBarrier与CountDownLatch的区别

作用不同:CyclicBarrier要等固定数量的线程都到达了栅栏位置才能继续执行,而CountDownLatch只需要等到数字为0,也就是CyclicBarrier作用于线程CountDownLatch作用于方法。

可重用性:CountDownLatch不可重复使用,而CyclicBarrier可以重复使用。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章