• 并发队列之:BlockingQueue和ConcurrentLinkedQueue


    一.并行和并发区别:

    • 并行:是指两者同时执行一件事。比如赛跑,两个人都在不停的往前跑;

    • 并发:是指资源有限的情况下,两者交替轮流使用资源。比如一段路(单核CPU资源)同时只能过一个人,A走一段后,让给B,B用完继续给A ,交替使用,目的是提高效率。

    二.什么叫线程安全

    • 线程安全就是说多线程访问同一代码,不会产生不确定的结果。

    • 反之线程不安全就是,多线程在访问同一代码时,会发生不确定因素,例如死锁,数据不一致性等。

    三.LinkedBlockingQueue

       LinkedBlockingQueue是一个线程安全的阻塞队列,它实现了BlockingQueue接口,BlockingQueue接口继承自java.util.Queue接口,并在这个接口的基础上增加了take和put方法,这两个方法正是队列操作的阻塞版本。

    由于LinkedBlockingQueue实现是线程安全的,实现了先进先出等特性,是作为生产者消费者的首选;LinkedBlockingQueue 可以指定容量,也可以不指定,不指定的话,默认最大是Integer.MAX_VALUE;其中主要用到put和take方法,put方法在队列满的时候会阻塞直到有队列成员被消费,take方法在队列空的时候会阻塞,直到有队列成员被放进来。

    示例代码:

    package com.lky.test;
    
    import java.util.concurrent.BlockingQueue;
    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.Executors;
    import java.util.concurrent.LinkedBlockingQueue;
    
    import org.apache.commons.logging.Log;
    import org.apache.commons.logging.LogFactory;
    
    import org.junit.Test;
    
    /**
     * @Title: testBlockQueue.java
     * @Package com.lky.test
     * @Description:多线程模拟实现生产者消费者模型(阻塞队列)
     * @author lky
     * @date 2015年10月24日 下午5:08:01
     * @version V1.0
     */
    public class testBlockQueue {
    
        private Log log = LogFactory.getLog(testBlockQueue.class);
    
        /**
         * @Title: testBlockQueue.java
         * @Package com.lky.test
         * @Description: 定义阻塞队列
         * @author lky
         * @date 2015年10月24日 下午5:07:28
         * @version V1.0
         */
        public class Basket {
            // 队列的最大容量为3
            BlockingQueue<String> basket = new LinkedBlockingQueue<String>(3);
    
            // 如果队列不满,则放入,否则阻塞等待
            public void produce(String apple) throws InterruptedException {
                basket.put(apple);
            }
    
            // 如果队列不为空,则取出,否则阻塞等待
            public String consumer() throws InterruptedException {
                return basket.take();
            }
        }
    
        /**
         * @Title: testBlockQueue.java
         * @Package com.lky.test
         * @Description: 定义生产者
         * @author lky
         * @date 2015年10月24日 下午5:18:17
         * @version V1.0
         */
        public class Produce implements Runnable {
            private Basket basket;
            private String fruit;
    
            public Produce(String fruit, Basket basket) {
                this.basket = basket;
                this.fruit = fruit;
            }
    
            @Override
            public void run() {
                try {
                    while (true) {
                        log.info("[" + Thread.currentThread().getName() + "]" + "开始生产apple----->" + this.fruit);
                        basket.produce(fruit);
                        log.info("apple生产完毕!!!!");
                        Thread.sleep(1000);
                    }
                } catch (Exception e) {
                    log.error("生产苹果异常!!!!!");
                }
            }
        }
    
        /**
         * @Title: testBlockQueue.java
         * @Package com.lky.test
         * @Description: 定义消费者
         * @author lky
         * @date 2015年10月24日 下午5:24:31
         * @version V1.0
         */
        public class Consumer implements Runnable {
            private Basket basket;
    
            public Consumer(Basket basket) {
                this.basket = basket;
            }
    
            @Override
            public void run() {
                try {
                    while (true) {
                        String fruit = basket.consumer();
                        log.info("[" + Thread.currentThread().getName() + "]" + "取到一个水果: " + fruit);
                        Thread.sleep(1000);
                    }
    
                } catch (Exception e) {
                    log.error("消费者取苹果异常!!!!");
                }
            }
        }
    
        @Test
        public void test() {
            System.out.println(Runtime.getRuntime().availableProcessors());//获取当前系统的cpu数目
            
            Basket basket = new Basket();
            Produce produce1 = new Produce("apple", basket);
            Produce produce2 = new Produce("banna", basket);
            Consumer consumer = new Consumer(basket);
    
            // 新建一个线程池
            ExecutorService service = Executors.newCachedThreadPool();
    
            service.submit(produce1);
            service.submit(produce2);
            service.submit(consumer);
    
            try {
                Thread.sleep(20000);
            } catch (Exception e) {
                log.error("程序异常错误!!!!");
            }
            service.shutdown();
        }
    }

    四 .ConcurrentLinkedQueue

         ConcurrentLinkedQueue是Queue的一个安全实现.Queue中元素按FIFO原则进行排序.采用CAS操作,来保证元素的一致性。

    示例代码:

    package com.lky.test;
    
    import java.util.concurrent.ConcurrentLinkedQueue;
    import java.util.concurrent.CountDownLatch;
    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.Executors;
    
    import org.apache.commons.logging.Log;
    import org.apache.commons.logging.LogFactory;
    
    /**
    * @Title: testNBlockQueue.java 
    * @Package com.lky.test 
    * @Description: 多线程模拟实现生产者消费者模型(非阻塞式队列)
    * @author lky 
    * @date 2015年10月24日 下午8:02:14 
    * @version V1.0
     */
    public class testNBlockQueue {
        private static Log log = LogFactory.getLog(testNBlockQueue.class);
        private static ConcurrentLinkedQueue<Integer> queue = new ConcurrentLinkedQueue<Integer>();
    
        private static int count = 2;
        private static CountDownLatch latch = new CountDownLatch(count);
    
        private static class Poll implements Runnable {
            @Override
            public void run() {
                    while (!queue.isEmpty()) {
                        log.info(Thread.currentThread().getName() + "消费一个商品: " + queue.poll());
                    }
                    latch.countDown();
            }
        }
    
        public static void main(String args[]) throws InterruptedException {
            long timeStart = System.currentTimeMillis();
            ExecutorService eService = Executors.newFixedThreadPool(4);
    
            // 生产商品
            for (int i = 0; i < 100000; ++i) {
                queue.offer(i);
            }
    
            // 消费者
            for (int i = 0; i < count; ++i) {
                eService.submit(new Poll());
            }
    
            latch.await();// 使得主线程阻塞,直到latch.getCount()为0
            System.out.println("Cost time: " + (System.currentTimeMillis() - timeStart));
            eService.shutdown();
        }
    }
          

         总结:在Java多线程应用中,队列的使用率很高,多数生产消费模型的首选数据结构就是队列(先进先出)。Java提供的线程安全的Queue可以分为阻塞队列和非阻塞队列,其中阻塞队列的典型例子是BlockingQueue,非阻塞队列的典型例子是ConcurrentLinkedQueue,在实际应用中要根据实际需要选用阻塞队列或者非阻塞队列。

  • 相关阅读:
    spring security 获取当前用户
    spring data jpa deleteInBatch 导致异常 java.lang.StackOverflowError
    大数据 分布式文件系统 HDFS概念
    angular9 ng start正常,build部署后无法正常加载页面
    springboot使用rocketmq RocketMQMessageListener参数
    spring boot使用rocketmq
    Teamcenter RAC 调用查询
    Teamcenter RAC 查找数据集并获取数据集中文件
    带有编译时和运行时的简记
    前台线程和后台线程、线程安全
  • 原文地址:https://www.cnblogs.com/dmir/p/4907515.html
Copyright © 2020-2023  润新知