Java并发JUC集合类

2023-11-03

文章目录

Java并发JUC集合类

为什么HashTable慢?它的并发度是什么?

Hashtable之所以效率低下主要是因为其实现使用了synchronized关键字对put等操作进行加锁,而synchronized关键字加锁是对整个对象进行加锁,也就是说在进行put等修改Hash表的操作时,锁住了整个Hash表,从而使得其表现的效率低下。

ConcurrentHashMap在JDK1.7和JDK1.8中实现有什么差别?JDK1.8解決了JDK1.7中什么问题

HashTable:使用了synchronized关键字对put等操作进行加锁;
ConcurrentHashMap JDK1.7:使用分段锁机制实现;
ConcurrentHashMap JDK1.8:则使用数组+链表+红黑树数据结构和CAS原子操作实现;

ConcurrentHashMap JDK1.7实现的原理是什么?

在JDK1.5~1.7版本,Java使用了分段锁机制实现ConcurrentHashMap
简而言之,ConcurrentHashMap在对象中保存了一个Segment数组,即将整个Hash表划分为多个分段;而每个Segment元素,它通过继承ReentrantLock来进行加锁,所以每次需要加锁的操作锁住的是一个Segment,这样只要保证每个Segment是线程安全的,也就实现了全局的线程安全;这样,在执行put操作时首先根据hash算法定位到元素属于哪个Segment,然后对该Segment加锁即可。因此,ConcurrentHashMap在多线程并发编程中可是实现多线程put操作。
在这里插入图片描述
concurrencyLevel:Segment数(并行级别、并发数)。默认是16,也就是说ConcurrentHashMap有16个Segments,所以理论上,这个时候,最多可以同时支持16个线程并发写,只要它们的操作分别分布在不同的Segment上。这个值可以在初始化的时候设置为其他值,但是一旦初始化以后,它是不可以扩容的。

ConcurrentHashMap JDK1.7是如何扩容的?

rehash(注:Segment数组不能扩容,扩容是Segment数组某个位置内部的数组HashEntry<K,V>[]进行扩容)

ConcurrentHashMap JDK1.8实现的原理是什么?

在JDK1.7之前,ConcurrentHashMap是通过分段锁机制来实现的,所以其最大并发度受Segment的个数限制。因此,在JDK1.8中,ConcurrentHashMap的实现原理摒弃了这种设计,而是选择了与HashMap类似的数组+链表+红黑树的方式实现,而加锁则采用CAS和synchronized实现。简而言之:数组+链表+红黑树,CAS

ConcurrentHashMap JDK1.8是如何扩容的?

tryPresize,扩容也是做翻倍扩容的,扩容后数组容量为原来的 2 倍。

ConcurrentHashMap JDK1.8是如何进行数据迁移的?

transfer,将原来的tab数组的元素迁移到新的nextTab数组中。

CopyOnWriteArrayList的实现原理?

CopyOnWriteArrayList是Java并发包中提供的一个并发容器,它是个线程安全的ArrayList,写操作通过创建底层数组的新副本来实现,是一种读写分离的并发策略,我们也可以称这种容器为"写时复制器",Java并发包中类似的容器还有CopyOnWriteSet,不过在CopyOnWriteSet中任然是调用的是CopyOnWriteArrayList。
实现原理:
集合框架中的ArrayList是非线程安全的,Vector虽是线程安全的,但是它的锁同步机制十分简单所以性能较差。
而CopyOnWriteArrayList则提供了另一种不同的并发处理策略(读写分离)。CopyOnWriteArrayList容器允许并发读,读操作是无锁的,性能较高。至于写操作,比如向容器中添加一个元素,则首先将当前容器复制一份,然后在新副本上执行写操作,结束之后再将原容器的引用指向新容器。

新增元素的操作:
首先获取锁然后生成一个新的数组+1,这时候再将新数组的新增位置添加新元素,最后替换原数组就行了。

public boolean add(E e) {
    final ReentrantLock lock = this.lock; //获取锁
    lock.lock();
    try {
        Object[] elements = getArray(); //获取原数组
        int len = elements.length; //获取原数组个数
        //拷贝一份原数组并+1,生成的新数组的len位置上增加元素
        Object[] newElements = Arrays.copyOf(elements, len + 1);
        newElements[len] = e;
        setArray(newElements); //替换原数组
        return true;
    } finally {
        lock.unlock(); //释放锁
    }
}

删除元素的操作:
首先获取锁,将删除的元素之外的其他元素拷贝到新数组中,然后切换引用,将原数组引用指向新数组。

public E remove(int index) {
     final ReentrantLock lock = this.lock; //获取锁
     lock.lock();
     try {
         Object[] elements = getArray(); //获取原数组,并得出当前数组大小
         int len = elements.length;
         E oldValue = get(elements, index); //获取旧值,并计算索引位置
         int numMoved = len - index - 1; //计算需要移动的元素个数
         if (numMoved == 0) //表示是末尾,直接拷贝原数组0-(len-1)
             setArray(Arrays.copyOf(elements, len - 1));
         else {
             Object[] newElements = new Object[len - 1]; //生成新数组并进行两次拷贝
             System.arraycopy(elements, 0, newElements, 0, index);
             System.arraycopy(elements, index + 1, newElements, index, numMoved);
             setArray(newElements);
         }
         return oldValue;
     } finally {
         lock.unlock(); //解锁
     }
 }

读取元素的操作:
不需要获取锁

public E get(int index) {
    return get(getArray(), index);
}

优点:
1、CopyOnWriteArrayList由于其"读写分离"的思想,遍历和修改操作分别作用在不同的数组,所以在使用迭代器进行遍历时候,不会抛出ConcurrentModificationException异常。
2、读操作性能很高,因为无需任何同步措施,比较适用于读多写少的并发场景
缺点:
1、内存占用问题,每次执行写操作都要将原容器拷贝一份,数据量大时,对内存压力较大,可能会引起频繁GC
2、无法保证实时性,Vector对于读写操作均加锁同步,可以保证读和写的强一致性。而CopyOnWriteArrayList的实现策略是写和读分别作用在新老不同数组上,在写操作执行过程中,读不会阻塞但读取到的却是为更新前的数据。

ConcurrentLinkedQueue的实现原理?

ConcurrentLinkedQueue是一个基于链接节点的无界线程安全队列,它采用先进先出的规则对节点进行排序,当我们添加一个元素的时候,它会添加到队列的尾部,当我们获取一个元素时,它会返回队列头部的元素。ConcurrentLinkedQueue采用了非阻塞方式实现。
ConcurrentLinkedQueue的结构
ConcurrentLinkedQueue由首节点Node head、尾节点Node tail组成,都用volatile修饰。而Node则由元素E item、下一节点Node next组成,都用volatile修饰。
每个节点(Node)由节点元素(item)和指向下一个节点的引用(next)组成,节点与节点之间就是通过这个next关联起来,从而组成一张链表结构的队列。
默认head节点元素为null,而tail等于head,即:head = tail = new Node(null)
在这里插入图片描述
入队,增加元素的操作
入队列就是将入队节点添加到队列的尾部,假设我们要在一个队列中依次插入4个节点,来看看下面的图来方便理解:
在这里插入图片描述
添加元素1:更新head节点的next节点为元素1节点,设置tail节点的next节点都指向元素1节点
添加元素2:设置元素1节点的next节点为元素2节点,然后更新tail节点指向元素2节点
添加元素3:设置元素2节点的next节点为元素3节点,然后设置tail节点的next节点为元素3节点
添加元素4:设置元素3节点的next节点为元素4节点,然后更新tail节点指向元素4节点

入队主要做两件事情:
第一是将入队节点设置成当前队列尾节点的下一个节点。
第二是更新tail节点,在入队列前如果tail节点的next节点不为空,则将入队节点设置成tail节点,如果tail节点的next节点为空,则将入队节点设置成tail的next节点,所以tail节点不总是尾节点。

public boolean offer(E e) {
    if (e == null) throw new NullPointerException();
    //入队前,创建一个入队节点
    Node<E> n = new Node<E>(e);
    retry:
    //死循环,入队不成功反复入队。
    for (;;) {
        //创建一个指向tail节点的引用
        Node<E> t = tail;
        //p用来表示队列的尾节点,默认情况下等于tail节点。
        Node<E> p = t;
        for (int hops = 0; ; hops++) {
        //获得p节点的下一个节点。
            Node<E> next = succ(p);
        //next节点不为空,说明p不是尾节点,需要更新p后在将它指向next节点
            if (next != null) {
               //循环了两次及其以上,并且当前节点还是不等于尾节点
                if (hops > HOPS && t != tail)
                    continue retry; 
                p = next;
            } 
            //如果p是尾节点,则设置p节点的next节点为入队节点。
            else if (p.casNext(null, n)) {
              //如果tail节点有大于等于1个next节点,则将入队节点设置成tair节点,更新失败了也没关系,因为失败了表示有其他线程成功更新了tair节点。
                if (hops >= HOPS)
                    casTail(t, n); // 更新tail节点,允许失败
                return true;  
            } 
           // p有next节点,表示p的next节点是尾节点,则重新设置p节点
            else {
                p = succ(p);
            }
        }
    }
}

第一步定位尾节点
tail节点并不总是尾节点,所以每次入队都必须先通过tail节点来找到尾节点,尾节点可能就是tail节点,也可能是tail节点的next节点。代码中循环体中的第一个if就是判断tail是否有next节点,有则表示next节点可能是尾节点。获取tail节点的next节点需要注意的是p节点等于p的next节点的情况,只有一种可能就是p节点和p的next节点都等于空,表示这个队列刚初始化,正准备添加第一次节点,所以需要返回head节点。获取p节点的next节点代码如下:

final Node<E> succ(Node<E> p) {
    Node<E> next = p.getNext();
    return (p == next) ? head : next;
}

第二步设置入队节点为尾节点
p.casNext(null, n)方法用于将入队节点设置为当前队列尾节点的next节点,p如果是null表示p是当前队列的尾节点,如果不为null表示有其他线程更新了尾节点,则需要重新获取当前队列的尾节点。
hops的设计意图
上面分析过对于先进先出的队列入队所要做的事情就是将入队节点设置成尾节点,doug lea写的代码和逻辑还是稍微有点复杂。那么我用以下方式来实现行不行?

public boolean offer(E e) {
    if (e == null)
        throw new NullPointerException();
    Node<E> n = new Node<E>(e);
    for (;;) {
        Node<E> t = tail;
        if (t.casNext(null, n) && casTail(t, n)) {
            return true;
        }
    }
}

让tail节点永远作为队列的尾节点,这样实现代码量非常少,而且逻辑非常清楚和易懂。但是这么做有个缺点就是每次都需要使用循环CAS更新tail节点。如果能减少CAS更新tail节点的次数,就能提高入队的效率,所以doug lea使用hops变量来控制并减少tail节点的更新频率,并不是每次节点入队后都将 tail节点更新成尾节点,而是当 tail节点和尾节点的距离大于等于常量HOPS的值(默认等于1)时才更新tail节点,tail和尾节点的距离越长使用CAS更新tail节点的次数就会越少,但是距离越长带来的负面效果就是每次入队时定位尾节点的时间就越长,因为循环体需要多循环一次来定位出尾节点,但是这样仍然能提高入队的效率,因为从本质上来看它通过增加对volatile变量的读操作来减少了对volatile变量的写操作,而对volatile变量的写操作开销要远远大于读操作,所以入队效率会有所提升。

还有一点需要注意的是入队方法永远返回true,所以不要通过返回值判断入队是否成功。

出队,删除元素的操作
出队列的就是从队列里返回一个节点元素,并清空该节点对元素的引用。让我们通过每个节点出队的快照来观察下head节点的变化:
在这里插入图片描述
如上图所示,是元素出队过程。
主要做两件事情:
第一是队列移除出队元素的Node。
第二是更新head节点。并不是每次出队时都更新head节点,当head节点里有元素时,直接弹出head节点里的元素,而不会更新head节点。只有当head节点里没有元素时,出队操作才会更新head节点。

public E poll() {
    Node<E> h = head;
   // p表示头节点,需要出队的节点
    Node<E> p = h;
    for (int hops = 0;; hops++) {
        // 获取p节点的元素
        E item = p.getItem();
        // 如果p节点的元素不为空,使用CAS设置p节点引用的元素为null,如果成功则返回p节点的元素。
        if (item != null && p.casItem(item, null)) {
            if (hops >= HOPS) {
                //将p节点下一个节点设置成head节点
                Node<E> q = p.getNext();
                updateHead(h, (q != null) ? q : p);
            }
            return item;
        }
        // 如果头节点的元素为空或头节点发生了变化,这说明头节点已经被另外一个线程修改了。那么获取p节点的下一个节点 
        Node<> next = succ(p);
        // 如果p的下一个节点也为空,说明这个队列已经空了
        if (next == null) {
          // 更新头节点。
            updateHead(h, p);
            break;
        }
        // 如果下一个元素不为空,则将头节点的下一个节点设置成头节点
        p = next;
    }
    return null;
}

// 更新head节点的updateHead方法:
final void updateHead(Node<E> h, Node<E> p) 
{
     // 如果两个结点不相同,尝试用CAS指令原子更新head指向新头节点
     if (h != p && casHead(h, p))
          //将旧的头结点指向自身以实现删除
     h.lazySetNext(h);
}

首先获取head节点的元素,并判断head节点元素是否为空,如果为空,表示另外一个线程已经进行了一次出队操作将该节点的元素取走,如果不为空,则使用CAS的方式将head节点的引用设置成null,如果CAS成功,则直接返回head节点的元素,如果CAS不成功,表示另外一个线程已经进行了一次出队操作更新了head节点,导致元素发生了变化,需要重新获取head节点。如果p节点的下一个节点为null,则说明这个队列为空(此时队列没有元素,只有一个伪结点p),则更新head节点。

判断队列是否为空
有些人在判断队列是否为空时喜欢用queue.size()==0,让我们来看看size方法:

public int size() 
{
     int count = 0;
     for (Node<E> p = first(); p != null; p = succ(p))
         if (p.item != null)
             // Collection.size() spec says to max out
             if (++count == Integer.MAX_VALUE)
                 break;
     return count;
 }

可以看到这样在队列在结点较多时会依次遍历所有结点,这样的性能会有较大影响,因而可以考虑empty函数,它只要判断第一个结点(注意不一定是head指向的结点)。

public boolean isEmpty() {
    return first() == null;
}

说说ConcurrentLinkedQueue的HOPS(延迟更新的策略)的设计?

通过对offer和poll方法的分析,我们发现tail和head是延迟更新的,两者更新触发时机为:
tail更新触发时机:当tail指向的节点的下一个节点不为null的时候,会执行定位队列真正的队尾节点的操作,找到队尾节点后完成插入之后才会通过casTail进行tail更新;当tail指向的节点的下一个节点为null的时候,只插入节点不更新tail。
head更新触发时机:当head指向的节点的item域为null的时候,会执行定位队列真正的队头节点的操作,找到队头节点后完成删除之后才会通过updateHead进行head更新;当head指向的节点的item域不为null的时候,只删除节点不更新head。
那么这样设计的意图是什么呢?
如果让tail永远作为队列的队尾节点,实现的代码量会更少,而且逻辑更易懂。但是,这样做有一个缺点,如果大量的入队操作,每次都要执行CAS进行tail的更新,汇总起来对性能也会是大大的损耗。如果能减少CAS更新的操作,无疑可以大大提升入队的操作效率,所以doug lea大师每间隔1次(tail和队尾节点的距离为1)进行才利用CAS更新tail。对head的更新也是同样的道理,虽然这样设计会多出在循环中定位队尾节点,但总体来说读的操作效率要远远高于写的性能,因此,多出来的在循环中定位尾节点的操作的性能损耗相对而言是很小的。

ConcurrentLinkedQueue适合什么样的使用场景?

ConcurrentLinkedQueue通过无锁来做到了更高的并发量,是个高性能的队列,但是使用场景相对不如阻塞队列常见,毕竟取数据也要不停的去循环,不如阻塞的逻辑好设计,但是在并发量特别大的情况下,是个不错的选择,性能上好很多,而且这个队列的设计也是特别费力,尤其的使用的改良算法和对哨兵的处理。整体的思路都是比较严谨的,这个也是使用了无锁造成的,我们自己使用无锁的条件的话,这个队列是个不错的参考。

什么是BlockingQueue?适合用在什么样的场景?

BlockingQueue通常用于一个线程生产对象,而另外一个线程消费这些对象的场景。
下图是对这个原理的阐述:
在这里插入图片描述
队列有容纳的临界点。也就是说,它是有限的。
负责生产的线程将会一直往队列里边插入新对象,如果生产线程尝试将对象放入队列时,该队列已经到达临界点,这个生产线程会一直处于阻塞之中,直到负责消费的线程从队列中拿走一个对象。
负责消费的线程将会一直从该阻塞队列中拿出对象,如果消费线程尝试去从一个空的队列中提取对象的话,这个消费线程将会处于阻塞之中,直到一个生产线程把一个对象丢进队列。

BlockingQueue实现例子?

这里是一个Java中使用BlockingQueue的示例。
本示例使用的是BlockingQueue接口的ArrayBlockingQueue实现。
1、Producer 类
注意它在每次put()调用时是如何休眠一秒钟的。这将导致Consumer在等待队列中对象的时候发生阻塞。

public class Producer implements Runnable{
   protected BlockingQueue queue = null;
   public Producer(BlockingQueue queue) {
       this.queue = queue;
   }
   public void run() {
      try {
          queue.put("1");
          Thread.sleep(1000);
          queue.put("2");
          Thread.sleep(1000);
          queue.put("3");
      } catch (InterruptedException e) {
          e.printStackTrace();
      }
   }
}

2、Consumer类
它只是把对象从队列中抽取出来,然后将它们打印到System.out

public class Consumer implements Runnable{
 protected BlockingQueue queue = null;
  public Consumer(BlockingQueue queue) {
      this.queue = queue;
  }
  public void run() {
      try {
          System.out.println(queue.take());
          System.out.println(queue.take());
          System.out.println(queue.take());
      } catch (InterruptedException e) {
          e.printStackTrace();
      }
  }
}

3、BlockingQueueExample
分别在两个独立的线程中启动了一个Producer和一个Consumer。

public class BlockingQueueExample {
    public static void main(String[] args) throws Exception {
        BlockingQueue queue = new ArrayBlockingQueue(1024);
        Producer producer = new Producer(queue);
        Consumer consumer = new Consumer(queue);
        new Thread(producer).start();
        new Thread(consumer).start();
        Thread.sleep(4000);
    }
}

什么是BlockingDeque?适合用在什么样的场景?

BlockingDeque类是一个双端队列,在不能够插入元素时,它将阻塞住试图插入元素的线程;在不能够抽取元素时,它将阻塞住试图抽取的线程。
deque(双端队列)是"Double Ended Queue"的缩写。因此,双端队列是一个你可以从任意一端插入或者抽取元素的队列。当线程既是一个队列的生产者又是这个队列的消费者的时候可以使用到BlockingDeque。如果生产者线程需要在队列的两端都可以插入数据,消费者线程需要在队列的两端都可以移除数据,这个时候也可以使用BlockingDeque。
BlockingDeque 图解:
在这里插入图片描述

BlockingDeque实现例子?

java.util.concurrent包提供了以下BlockingDeque接口的实现类:LinkedBlockingDeque

BlockingDeque<String> deque = new LinkedBlockingDeque<String>();
deque.addFirst("1");
deque.addLast("2");
String two = deque.takeLast();
String one = deque.takeFirst();
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Java并发JUC集合类 的相关文章

  • 涉及数学的方法给出与计算器不同的答案

    我是java新手 所以请耐心等待 我试图从比赛总数中获得胜利的百分比 但我正在做的事情还很遥远 我获取百分比的方法如下 public double winPercentage int wins int total return wins t
  • Java 中具有级别顺序插入的完整二叉搜索树

    我们接到一个任务 需要编码 二叉搜索树 那个树has to be complete not perfect 这意味着所有不在最低级别或次低级别的节点都应该有 2 个子节点 而最低级别的节点应尽可能远离左侧 我们需要插入到树中等级顺序 所以如
  • 通过蓝牙将字符串从作为客户端的 PC 发送到作为服务器的移动设备

    我需要通过蓝牙将字符串从 PC 传输到 Android 移动设备的帮助 Android 移动设备应充当服务器并在设备屏幕上显示字符串消息 作为客户端的 PC 应该将字符串发送到移动设备 我希望服务器对提取的字符串 通过蓝牙传输 做出反应 这
  • 如何通过双击图标来执行JAVA程序?

    我写了一个java程序 现在我想在没有 IDE Eclipse 等的情况下打开我的控制台 java 应用程序 只需双击桌面上的可执行版本即可 我已将 java 项目导出为 Runnable JAR 文件 但无法打开 当我尝试使用cmd打开应
  • 从java应用程序发送电子邮件时出现异常:中继被拒绝

    我们正在使用 Spring Mail 从 java 应用程序发送电子邮件 org springframework mail javamail JavaMailSenderImpl Spring电子邮件配置是
  • Java Swing 自定义形状(2D 图形)

    我需要绘制自定义形状 现在 当用户单击面板上的几个点时 我使用多边形创建一个形状 public void mouseClicked MouseEvent e polygon addPoint e getX e getY repaint 但我
  • 创建UML图时应该编写构造函数吗?

    我有一项作业要求我为实际的 Java 程序创建 UML 图 但程序中有几个构造函数方法 我很困惑 我是否应该将这些构造函数方法添加到图中 根据 UML 规范 2 5 版第 11 4 4 节 构造函数是一个具有所属类类型的单个返回结果参数的操
  • 有没有办法在@Service上使用@ControllerAdvice

    我有一个项目需求 但我没有任何需求 Controller or RestController但我需要为我的服务层提供一个全局异常处理程序 所以我需要配置 ControllerAdvice on Service 请告诉我是否还有其他方法可以做
  • 如何从网上获取源代码?

    我正在尝试从 Web 获取 HTML 源代码 我尝试这样做 u new URL url URLConnection con u openConnection con setRequestProperty User Agent Mozilla
  • 无法安装 JDK 9,因为“另一个 Java 安装正在进行中”

    我已经在 Windows 10 x64 上使用 JDK 9 一段时间了 但是当我去安装最新的早期版本 b174 时 我首先卸载了以前的版本 像往常一样 然后运行新的安装程序 它失败并显示一个消息框 显示 另一个 Java 安装正在进行中 您
  • 使用 JNDI 添加 LDAP 条目

    我正在尝试使用 JNDI 将条目添加到 LDAP 服务器 我可以成功地从 LDAP 服务器读取条目 但是当我尝试添加新条目时出现错误 我检查了各种方法但都失败了 private String getUserAttribs String se
  • Java 8 中函数类型全等 lambda 表达式的用法

    我对 的定义和用法感到困惑 Stream collect Supplier
  • 在 Tomcat 中触发内部 ServletRequest

    我正在使用 Quartz 来安排 Web 应用程序的后台任务 其中一些任务只是针对同一 Web 应用程序发出请求 我想避免依赖于任何类型的网络设置 例如 如果从数据中心内发出带有我自己域名的请求 则可能无法正确路由 是否有一个 Java A
  • 错误:找不到符号 ArrayList

    我正在尝试创建某种列表来存储数组 表 中的值 我在这里使用数组列表 但我应该使用列表吗 但是 每次我尝试编译时 它都会引发以下错误 找不到标志 符号 ArrayList类 位置 玩家类 TablePlayer 代码如下 public cla
  • 是否有适合 Java 1.4 和 SE (Swing) 应用程序的优秀 DI 框架?

    我正在寻找一个适用于在 JDK 1 4 下运行的 Java SE Swing 应用程序的依赖注入框架 有没有我可以使用的推荐 DI 框架 Guice 和其他基于注释的框架已经退出 我不想搞乱像 Retroweaver 这样的东西 另外 Sp
  • Android 自定义相机 - 在矩形内裁剪图像

    我有一个自定义相机应用程序 它有一个居中的矩形视图 如下所示 当我拍照时 我想忽略矩形之外的所有内容 该视图与我的 XML 视图中的 Camera Preview 或 SurfaceView 没有任何联系 如下所示
  • Xuggler 未转换 .webm 文件?

    我只是尝试使用 Xuggler 将 mov 文件转换为 webm 这应该可以工作 因为 FFMPEG 支持 webm 文件 这是我的代码 IMediaReader reader ToolFactory makeReader home use
  • 寻找基于循环固定大小数组的双端队列

    我正在寻找一个Deque其具有以下特点 它有固定的大小 如果我在头 尾添加元素 则另一端的元素会丢失 它是基于数组的 所以我可以在恒定时间内访问随机元素 我可以在前面或末尾添加元素 双端队列 我检查了Deque的实施JCF但我没有找到任何合
  • Java中不同格式的字符串解析为日期

    我想转换String to Date以不同的格式 例如 我从用户那里得到 String fromDate 19 05 2009 i e dd MM yyyy format 我想转换这个fromDate作为日期对象 yyyy MM dd fo
  • JVM锯齿状空闲进程

    我目前正在进行一项涉及 JVM 及其内存使用工作原理的研究 我不明白的是 JVM在空闲时用什么填充它的内存 只是为了在堆几乎达到时释放它 为什么使用的内存不只有一条平线 顺便说一句 这个 java 应用程序托管在 glassfish 上 但

随机推荐

  • fastboot一键进入9008_【高通9008】究竟是怎么一回事?

    转自 河北阮咸科技有限公司 这里说的高通 是指采用高通品牌CPU的安卓手机 高通就是近两年和华为争5G标准的那个美国公司 国内众多的品牌 如 小米 oppo vivo 华为 对 华为也有一部分机器采用了高通的CPU 只要是高通的CPU就会有
  • jsp导入所需要的JavaBean方法

    在jsp中可以使用指令导入所需要的包 或者使用
  • OSTU算法

    OSTU算法目的就是计算出一连通区域的阈值 然后对该区域二值化 Ostu算法是一种用于二值化最佳阈值的选取方法 基本原理是根据阈值T将图像中的像素点分为C1和C2两类 不断的调整阈值T之后若此时两类之间存在最大的类间方差 那么此阈值即是最佳
  • Flutter开发报错uses-sdk:minSdkVersion 16 cannot be smaller than version 19 declared in library

    文章目录 问题描述 问题原因 解决方法 gradle文件介绍 项目级别的build gradle 模块级别的build gradle 问题描述 今天导入一个新项目时报错 报错内容如下 uses sdk minSdkVersion 16 ca
  • 吴恩达机器学习(六)线性回归的梯度下降

    线性回归的梯度下降 线性回归模型 线性假设函数 平方差代价函数 梯度下降算法可以优化J 0 1 最小平方差代价函数 实现好的梯度下降算法的关键 在于在导数项 线性回归的代价函数 形状总是凸函数 convex 只有一个全局最优解 在梯度下降的
  • 完整的性能测试流程

    前言 本篇文章带大家一起学习性能测试 1 使用jmeter工具进行接口的性能压测 2 熟练使用jmeter工具 3 针对项目接口进行压测 性能测试的流程和操作的过程介绍 4 分析调优 拓展部分 一 关于性能测试的理论介绍 为什么要进行性能测
  • MyEclipse的properties配置文件中文显示为unicode问题解决方法

    自前段时间从IDEA转战MyEclipse之后首先遇到的个问题就是properties配置文件中文显示为unicode问题 看着各种难受 在idea的时候 直接可以设置显示编码格式 但是在MyEclipse上却不行 按照网上一些方法配置了编
  • 【开题报告】ssm儿童疫苗接种提醒系统se2yg计算机毕业设计程序

    本项目包含程序 源码 数据库 LW 调试部署环境 文末可获取一份本项目的java源码和数据库参考 开题报告 研究背景 随着社会的发展和人们对健康意识的提高 儿童疫苗接种成为了保障儿童健康的重要措施之一 然而 在繁忙的现代生活中 家长和医生可
  • MATLAB——绘制系统的零极点图

    题目1 已知系统函数 H s s
  • @Setter与@Getter注解无效,以及idea无法搜索插件的解决方案

    原因 1 lombok插件未安装 2 lombok插件损坏 解决方案 重新安装lombok插件 步骤 进入idea点击File gt Settings gt 搜索点击Plugins 进入插件界面 在搜索框中输入lombok安装 若无法搜索到
  • Gradle 复制文件 无知的菜墩

    Gradle 复制文件 很多大佬都是直接贴出如下代码 简单说就是 task 直接使用 from into include 等方法 然而直接复制下来之后 过了俩小时零一秒钟后依然无法使用 一直不生效 task nestedSpecs type
  • Go Facade外观(门面)设计模式

    动机 Motivation 上述A方案的问题在于组件的客户和组件中各种复杂的子系统有了过多的耦合 随着外部客户程序和各子系统的演化 这种过多的耦合面临很多变化的挑战 如何简化外部客户程序和系统间的交互接口 如何将外部客户程序的演化和内部子系
  • hbuilderx自定义常用代码块

    apipost body let res await this http post 1 if res code 200 2 else this toast res msg prefix apipost project uni app sco
  • hdu 5831 Rikka with Parenthesis II 2016 Multi-University 8

    Problem acm hdu edu cn showproblem php pid 5831 题意 给个括号序列 问能不能通过一次把两个不同位置的符号交换的操作 使得序列里的所有括号左右配对合法 分析 左括号进栈 如果是右括号而且栈顶是左
  • Linux性能监控命令_sar & 自动保存30天历史信息

    简介 sar命令将操作系统中选定的累积活动计数器的内容写入标准输出 计费系统根据 count 和 interval参数中的值 以秒为单位 按照指定的时间间隔写入指定次数的信息 目录 1 语法 1 1 常用参数 2 常见用法 2 1 监控CP
  • HTTP->WebRTC演进路径

    first HTTP Pre AJAX 原始web 一页里发送请求后才返回另一页 如Geocities second AJAX 2004 更新页面不需要刷新 如GMail third Web Sockets 2008 页面能建立双向通信 通
  • Android 10 暗黑模式适配,你需要知道的一切

    在 Android 10 里 Dark theme 暗黑模式得到了系统级的支持 暗黑模式不仅酷炫 而且有降低屏幕耗电 在光线较暗的环境中使用更舒适等好处 今天带大家看一下如何适配暗黑模式 本文会从以下几点进行介绍 动态开启暗黑模式 使用 D
  • 解密IP地址的不同潜力与应用场景

    作为专业爬虫代理供应商 我们经常需要面对不同的IP地址需求 在IP地址选择中 动态IP和静态IP是两个常见的选项 但究竟什么是动态IP和静态IP 它们有什么区别和优势 适用于哪些场景 本文将为你详细解答 让你对IP地址有更全面的了解 第一部
  • html文字图片同一行,CSS控制图片和文字在同一行显示且对齐的3种方法

    CSS控制图片和文字在同一行显示且对齐的3种方法 在 HTML 代码中 有时会需要在文字旁边加上一个图标 默认情况 是图片置顶对齐 文字置底对齐 所以通常图片高 文字低 不能水平居中对齐 常见欢思中属餐显近和想都性厅示近和想都性厅示方法有3
  • Java并发JUC集合类

    文章目录 Java并发JUC集合类 为什么HashTable慢 它的并发度是什么 ConcurrentHashMap在JDK1 7和JDK1 8中实现有什么差别 JDK1 8解決了JDK1 7中什么问题 ConcurrentHashMap