• java多线程系列11 juc包下的队列


     队列分为两类

     阻塞队列

    BlockingQueue提供如下两个支持阻塞的方法:

      (1)put(E e): 尝试把e元素放如BlockingQueue中,如果该队列的元素已满,则阻塞该线程。

      (2)take(): 尝试从BlockingQueue的头部取出元素,如果该队列的元素已空,则阻塞该线程。

        jdk实现的有以下几种:

        ArrayBlockingQueue,底层是数组,有界队列

        LinkedBlockingQueue,,底层是链表,无界队列

        PriorityBlockingQueue,一个带优先级的队列,上put时是不会受阻的,但是如果队列为空,取元素的操作take就会阻塞。另外,往入该队列中的元 素要具有比较能力。

         PriorityBlockingQueue里面存储的对象必须是实现Comparable接口。队列通过这个接口的compare方法确定对象的priority。 【下面会有代码举例说明】

        SynchronousQueue 是一个没有数据缓冲的阻塞队列,(只能存储一个元素)准确的说 他不存储元素,放入元素必须等待取走以后,才能放入新的元素

        但它的特别之处在于它内部没有容器,一个生产线程,当它生产产品(即put的时候),如果当前没有人想要消费产品(即当前没有线程执行take),此生产线程必须阻塞,等待一个消费线程调用take操作,take操作将会唤醒该生产线程,同时消费线程会获取生产线程的产品(即数据传递),这样的一个过程称为一次配对过程(当然也可以先take后put,原理是一样的)。另外他是直接使用CAS实现线程的安全访问(jdk8)。

        DelayQueue, 只有在队列中的元素到期后才能取出。里面存储的对象必须实现Delayed接口,里面有两个方法【下面会有代码举例说明】

     非阻塞队列

      ConcurrentLinkedQueue 性能最好,是一个无界队列

       add 和 offer都是加入元素方法(没有差别)

       poll和peek都是获取头元素的方法,区别在于前者会删除元素,后者不会删除

     下面举例说明PriorityBlockingQueue 和 DelayQueue的用法

      

    /**
     * PriorityBlockingQueue测试
     * 
     *
     */
    public class TestPriorityQueue2 {
    	public static void main(String[] args) throws InterruptedException {  
            PriorityBlockingQueue<Student>  priorityBlockingQueue=new PriorityBlockingQueue<>();  
            priorityBlockingQueue.put(new Student("stu1",2));  
            priorityBlockingQueue.put(new Student("stu2",3));  
            priorityBlockingQueue.put(new Student("stu3",2));  
            priorityBlockingQueue.put(new Student("stu4",4));  
         
      
            System.out.println(priorityBlockingQueue.take().toString());  
        }  
    }
     class Student implements Comparable<Student> {
        private String name;
        private  Integer age;
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public Integer getAge() {
            return age;
        }
    
        public void setAge(Integer age) {
            this.age = age;
        }
        
        public Student(String name, Integer age) {
            this.name = name;
            this.age = age;
        }
    
    //     //正序
    //    @Override
    //    public int compareTo(Student o) {
    //        return  this.age>o.age?1:this.age<o.age?-1:0;
    //    }
    
        //逆序
       @Override
       public int compareTo(Student o) {
           return  this.age<o.age?1:-1;
       }
        @Override
        public String toString() {
            return "Student{" +
                    "name=" + name + ", age=" + age + '}';
        }
    
    	
    
    	
    }
    

      接下来是DelayQueue,主要应用场景有以下几个

    1) 关闭空闲连接。服务器中,有很多客户端的连接,空闲一段时间之后需要关闭之。
    2) 缓存。缓存中的对象,超过了空闲时间,需要从缓存中移出。
    3) 任务超时处理。在网络协议滑动窗口请求应答式交互时,处理超时未响应的请求。

    下面举一个例子 

    public class Wangming implements Delayed {
    
    	private String name;
    	// 截止时间
    	private long endTime;
    
    	public Wangming(String name, String id, long endTime) {
    		this.name = name;
    		this.endTime = endTime;
    	}
    
    	public Wangming(String name, long endTime) {
    		this.name = name;
    		this.endTime = endTime;
    	}
    
    	public String getName() {
    		return this.name;
    	}
    
    	/**
    	 * 用来判断是否到了截止时间
    	 */
    	@Override
    	public long getDelay(TimeUnit unit) {
    		return endTime - System.currentTimeMillis();
    	}
    
    	/**
    	 * 相互比较排序用,延迟队列队列头总是存储快要过期的数据
    	 */
    	@Override
    	public int compareTo(Delayed o) {
    		Wangming wangming = (Wangming) o;
    		return endTime - wangming.endTime > 0 ? 1 : -1;
    	}
    
    	@Override
    	public String toString() {
    		return "Wangming [name=" + name + ", endTime=" + endTime + "]";
    	}
    
    	public static void main(String[] args) {
    		DelayQueue<Wangming> queue = new DelayQueue<Wangming>();
    		Wangming man = new Wangming("a", 1000 * 11 + System.currentTimeMillis());
    		Wangming man1 = new Wangming("b", 1000 * 3 + System.currentTimeMillis());
    		Wangming man2 = new Wangming("c", 1000 * 2 + System.currentTimeMillis());
    		queue.add(man);
    	
    		queue.add(man1);
    		queue.add(man2);
    		try {
    			while (true) {
    				System.out.println(queue.take());
    			}
    		} catch (InterruptedException e) {
    			e.printStackTrace();
    		}
    	}
    
    }
    

      

     下面简单说明下DelayQueue的原理

    DelayQueue内部的实现使用了一个优先队列。当调用DelayQueue的offer方法时,把Delayed对象加入到优先队列q中。如下:

    public boolean offer(E e) {
            if (e == null)
                throw new NullPointerException();
            modCount++;
            int i = size;
            if (i >= queue.length) //长度不够扩容
                grow(i + 1);
            size = i + 1;
            if (i == 0) //还没有元素 会直接加入
                queue[0] = e;
            else  //有元素了,排序加入 加入顺序跟你的比较器有关
                siftUp(i, e);
            return true;
        }
    

      关于take()方法

         

     public E take() throws InterruptedException {
            final ReentrantLock lock = this.lock;
            lock.lockInterruptibly();
            try {
                for (;;) {
                    E first = q.peek();
                    if (first == null)
                        available.await();
                    else {
                        long delay = first.getDelay(NANOSECONDS);
                        if (delay <= 0)
                            return q.poll();
                        first = null; // 防止内存泄漏
                        if (leader != null)
                            available.await();
                        else {
                            Thread thisThread = Thread.currentThread();
                            leader = thisThread;
                            try {
                                available.awaitNanos(delay);
                            } finally {
                                if (leader == thisThread)
                                    leader = null;
                            }
                        }
                    }
                }
            } finally {
                if (leader == null && q.peek() != null)
                    available.signal();
                lock.unlock();
            }
        }
    

      基本流程比较简单 ,主要这里需要注意2点

       first  = null  //主要是为了防止内存泄漏
       leader  减少等待时间

  • 相关阅读:
    Python 描述符(descriptor) 杂记
    Celery 使用简介
    异步任务神器 Celery 简明笔记
    高性能框架gevent和gunicorn在web上的应用及性能测试
    Flask + Gunicorn + Nginx 部署
    Mysql查看最大连接数和修改最大连接数
    配置 influxDB 鉴权及 HTTP API 写数据的方法
    Java 字符串拼接 五种方法的性能比较分析 从执行100次到90万次
    linux端口开放指定端口的两种方法
    java自带的监控工具VisualVM一
  • 原文地址:https://www.cnblogs.com/javabigdata/p/6964219.html
Copyright © 2020-2023  润新知