zoukankan      html  css  js  c++  java
  • Java并发编程(四)

    1、线程上下文切换

      线程的上下文切换:任务的状态保存及在加载。

    • 上下文:线程切换时CPU寄存器和程序计数器所保存的当前线程信息。
    • 寄存器:CPU内部容量较小但速度很快的内存区域。寄存器通过对常用值的快速访问来加快计算机程序运行的速度。
    • 程序计数器:一个专门的寄存器,用于表明指令序列中CPU正在执行的位置。存储的值为正在执行的指令的位置或下一个将被执行的指令的位置。

    1.1 上下文切换

      上下文切换:内核在CPU上对进程或线程进行切换。切换流程如下:‘’

      (1)挂起一个进程,将这个进程在CPU中的状态存储于内存的PCB(进程控制块)中。

      (2)在PCB中检索下一个进程的上下文并将其在CPU的寄存器中恢复。

      (3)跳转到程序计数器所指向的位置并恢复该进程。

    1.2 引起线程上下文切换的原因

      (1)当前正在执行的任务完成,系统的CPU正常调度下一个任务。

      (2)当前正在执行的任务遇到I/O等阻塞操作,调度器挂起此任务,继续调度下一个任务。

      (3)多个任务并发抢占锁资源,当前任务没有抢到锁资源,被调度器挂起,继续调度下一个任务。

      (4)用户代码挂起当前任务,如执行sleep方法,让出CPU。

      (5)硬件中断。

    2、Java阻塞队列

      队列是一种只允许在表的前端进行删除操作,在表的后端进行插入操作的线性表。

      阻塞是指操作队列的线程的一种状态。线程阻塞有如下两种情况:

    • 消费者阻塞:在队列为空时,消费者端的线程都会被自动阻塞,直到数据放入队列,消费者线程会被自动唤醒并消费数据。如图。

    • 生产者阻塞:在队列已满且没有可用空间时,生产者端的线程都会被自动阻塞,直到队列中有空的位置腾出,线程会被自动唤醒并生产数据。如图。

    2.1 阻塞队列的主要操作

    2.1.1 插入操作

      (1)public abstract bollean add(E param):指定的元素插入队列中,成功返回true,如果没有可用空间,则抛出IllegalStateException。源码如下:

        public boolean add(E e) {
            if (offer(e))
                return true;
            else
                throw new IllegalStateException("Queue full");
        }

      (2)public abstract bollean offer(E param):将指定元素插入队列中,在成功时返回true,如果当前没有可用的空间,则返回false。源码如下:

       public boolean offer(E e) {
            checkNotNull(e);
            final ReentrantLock lock = this.lock;
            lock.lock();
            try {
                if (count == items.length)
                    return false;
                else {
                    enqueue(e);
                    return true;
                }
            } finally {
                lock.unlock();
            }
        }

      enqueue(e)方法源码如下:

        private void enqueue(E x) {
            // assert lock.getHoldCount() == 1;
            // assert items[putIndex] == null;
            final Object[] items = this.items;
            items[putIndex] = x;
            if (++putIndex == items.length)
                putIndex = 0;
            count++;
            notEmpty.signal();
        }

      (3)public boolean offer(E e, long timeout, TimeUnit unit ) throws InterruptedException:将指定的元素插入队列中,可以设定等待时间,如果超时,则返回false。源码如下:

        public boolean offer(E e, long timeout, TimeUnit unit)
            throws InterruptedException {
    
            checkNotNull(e);
            long nanos = unit.toNanos(timeout);
            final ReentrantLock lock = this.lock;
            lock.lockInterruptibly();
            try {
                while (count == items.length) {
                    if (nanos <= 0)
                        return false;
                    nanos = notFull.awaitNanos(nanos);
                }
                enqueue(e);
                return true;
            } finally {
                lock.unlock();
            }
        }

      (4)public void put(E e) throws InterruptedException:将指定元素插入队列,如果队列已经满了,则阻塞、等待可用队列空间的释放,直到有可用的队列空间释放且插入成功为止,源码如下:

        public void put(E e) throws InterruptedException {
            checkNotNull(e);
            final ReentrantLock lock = this.lock;
            lock.lockInterruptibly();
            try {
                while (count == items.length)
                    notFull.await();
                enqueue(e);
            } finally {
                lock.unlock();
            }
        }

    2.1.2 插入操作

      (1)poll():取走队列队首的对象,如果取不到,则返回null。源码如下:

       public E poll() {
            final ReentrantLock lock = this.lock;
            lock.lock();
            try {
                return (count == 0) ? null : dequeue();
            } finally {
                lock.unlock();
            }
        }

      dequeue()源码如下:

        private E dequeue() {
            // assert lock.getHoldCount() == 1;
            // assert items[takeIndex] != null;
            final Object[] items = this.items;
            @SuppressWarnings("unchecked")
            E x = (E) items[takeIndex];
            items[takeIndex] = null;
            if (++takeIndex == items.length)
                takeIndex = 0;
            count--;
            if (itrs != null)
                itrs.elementDequeued();
            notFull.signal();
            return x;
        }

      (2)poll(long timeout, TimeUnit unit):取走队列队首的对象如果在指定的时间内有数据可取,则返回队列中的数据,若超时,返回null。

      (3)take():取走队列队首的对象,如果队列为空,则进入阻塞状态等待,直到队列中有新的数据被加入,取出新加入的数据。源码如下:

        public E take() throws InterruptedException {
            final ReentrantLock lock = this.lock;
            lock.lockInterruptibly();
            try {
                while (count == 0)
                    notEmpty.await();
                return dequeue();
            } finally {
                lock.unlock();
            }
        }

      (4)drainTo(Collection collection):一次性批量获取所有数据,也可以指定获取数据的个数。源码如下:

         public int drainTo(Collection<? super E> c, int maxElements) {
            checkNotNull(c);
            if (c == this)
                throw new IllegalArgumentException();
            if (maxElements <= 0)
                return 0;
            final Object[] items = this.items;
            final ReentrantLock lock = this.lock;
            lock.lock();
            try {
                int n = Math.min(maxElements, count);
                int take = takeIndex;
                int i = 0;
                try {
                    while (i < n) {
                        @SuppressWarnings("unchecked")
                        E x = (E) items[take];
                        c.add(x);
                        items[take] = null;
                        if (++take == items.length)
                            take = 0;
                        i++;
                    }
                    return n;
                } finally {
                    // Restore invariants even if c.add() threw
                    if (i > 0) {
                        count -= i;
                        takeIndex = take;
                        if (itrs != null) {
                            if (count == 0)
                                itrs.queueIsEmpty();
                            else if (i > take)
                                itrs.takeIndexWrapped();
                        }
                        for (; i > 0 && lock.hasWaiters(notFull); i--)
                            notFull.signal();
                    }
                }
            } finally {
                lock.unlock();
            }
        }

    2.2 Java中的阻塞队列实现

      

      (1)ArrayBlockingQueue:基于数组结构实现的有界阻塞队列,ArrayBlockingQueue队列按照先进先出原则对元素进行排序,在默认情况下不保证元素操作的公平性。

      (2)LinkedBlockingQueue:基于链表结构实现的有界阻塞队列,LinkedBlockingQueue按照先进先出原则对元素进行排序。LinkedBlockingQueue对生产者端和消费者端分别采用了两个独立的锁来控制数据同步,队列的并发性能较高。

      (3)PriorityBlockingQueue:一个支持优先级的无界队列。元素在默认情况下采用升序排列,可以自定义实现compareTo方法来指定元素进行排序规则。如果两个元素的优先级相同,则不能保证该元素的存储和访问顺序。

    class Data implements Comparable<Data> {
        private String id;
        private Integer number;
    
        public Integer getNumber() {
            return number;
        }
        public void setNumber(Integer number) {
            this.number = number;
        }
        @Override
        public int compareTo(Data o) {
            return this.number.compareTo(o.getNumber());
        }
    }
    
    final PriorityBlockingQueue<Data> queue = new PriorityBlockingQueue<Data>();

      (4)DelayQueue:支持延时获取元素的无界阻塞队列,底层使用PriorityBlockingQueue实现。DelayQueue队列中的元素必须实现Delayed接口,该接口定义了在创建元素是该元素的延迟时间,在内部通过为每个元素的操作加锁来保障数据的一致性。只有在延迟时间到后才能从队列中提取元素。

      DelayQueue适用于以下场景:

    • 缓存系统的设计:可以用DelayQueue保存缓存元素的有效期,使用一个线程循环查询DelayQueue,一旦能从DelayQueue中获取元素,则表示缓存的有效期到了。
    •  定时任务调度:使用DelayQueue保存即将执行的任务和执行时间,一旦从DelayQueue中获取元素,就表示任务开始执行。

      (5)SynchronousQueue:一个不存储元素的阻塞队列。SynchronousQueue中的每个put操作都必须等待一个take操作完成,否则不能继续添加元素。

      (6)LinkedTransferQueue:基于链表结构实现的无界阻塞队列。

      相对于其他阻塞队列,LinkedTransferQueue多了transfer、tryTransfer和tryTransfer(E e, long timeout, TimeUnit unit)方法。

    • transfer方法:如果当前有消费者正在等待接收元素,transfer方法就会直接把生产者传入的元素投递给消费者并返回true。如果没有消费者在等待接收元素,transfer方法会将元素存放在队列的尾部(tail)节点,直到被消费后才返回。
    • tryTransfer方法:尝试是否能将生产者传入的元素直接传给消费者,如果没有消费者等待接收元素,则返回false。
    • tryTransfer(E e, long timeout, TimeUnit unit)方法:尝试是否能将生产者传入的元素直接传给消费者,如果没有消费者,则等待指定时间,超时返回false。

      (7)LinkedBlockingDeque:基于链表结构实现的双向阻塞队列,可以在队列的两端分别执行插入和移除元素操作。可以减少一半的锁资源竞争,提高队列的操作效率。相比于其他队列,LinkedBlockingDeque多了addFirst、addLast、offerFirst、offerLast、peekFirst、peekLast等方法。其中以First结尾的方法表示在队列头部执行插入、获取、移除操作;以Last结尾的方法表示在队列的尾部执行插入、获取、移除操作。

  • 相关阅读:
    日志组件一:Log4j
    HTTPS加密那点事--轻松秒懂HTTPS非对称加密
    图解Git
    Python 迭代器 & __iter__方法
    Fiddler 抓包工具总结
    Python使用struct处理二进制(pack和unpack用法)
    Python binascii
    常见证书格式及相互转换
    MyBatis Generator 详解
    MyBatis学习总结(八)——Mybatis3.x与Spring4.x整合
  • 原文地址:https://www.cnblogs.com/strong-FE/p/13284420.html
Copyright © 2011-2022 走看看