1 ConcurrentLinkedQueue

1.1 简介

  它是一个基于链接节点的无界线程安全队列。此队列按照 FIFO(先进先出)原则对元素进行排序。
  新的元素插入到队列的尾部,队列获取操作从队列头部获得元素。

  当多个线程共享访问一个公共 collection 时,ConcurrentLinkedQueue 是一个恰当的选择,相对于list,它在多线程环境下是安全的。

  

1.2 构造方法

1.3 常用方法

1.4 isEmpty、size、contains、toArray方法

private static Queue<String> m1() throws InterruptedException {
Queue<String> q = new ConcurrentLinkedQueue<String>();
System.out.println("队列是否为空" + q.isEmpty());
System.out.println("队列大小" + q.size()); for (int i = 0; i < 2 ; i++) {
Thread t = new Thread(() -> {
for (int j = 0; j < 10; j++) {
q.offer( j+""); }
}, "写线程:" + i);
t.start();
t.join();
}
System.out.println("队列是否为空" + q.isEmpty());
System.out.println("队列大小" + q.size());
System.out.println("是否包含5:" + q.contains("5"));
Object[] objects = q.toArray();
for (int i = 0; i < objects.length; i++) {
System.out.println(objects[i]);
}
return q;
}

输出

队列是否为空true
队列大小0
队列是否为空false
队列大小20
是否包含5:true
0
1
2
3
4
5
6
7
8
9
0
1
2
3
4
5
6
7
8
9 Process finished with exit code 0

1.5 offer、peek、poll

private static Queue<String> m2() throws InterruptedException {
Queue<String> q = new ConcurrentLinkedQueue<String>(); for (int i = 0; i < 2 ; i++) {
Thread t = new Thread(() -> {
for (int j = 0; j < 10; j++) {
q.offer(Thread.currentThread().getName() + " 值:" + j);
System.out.println(Thread.currentThread().getName() + "添加值" + j);
}
}, "写线程:" + i);
t.start();
t.join();
} new Thread(()->{
for (int j = 0; j < 3; j++) {
String poll = q.peek();
System.out.println("读取头元素值不删除头元素:" + poll);
}
}).start(); for (int i = 0; i < 2 ; i++) {
new Thread(()->{
for (int j = 0; j < 10; j++) {
String poll = q.poll();
System.out.println("读取头元素值并删除头元素:" + poll);
}
},"读线程:" + i).start();
}
return q;
}

输出

写线程:0添加值0
写线程:0添加值1
写线程:0添加值2
写线程:0添加值3
写线程:0添加值4
写线程:0添加值5
写线程:0添加值6
写线程:0添加值7
写线程:0添加值8
写线程:0添加值9
写线程:1添加值0
写线程:1添加值1
写线程:1添加值2
写线程:1添加值3
写线程:1添加值4
写线程:1添加值5
写线程:1添加值6
写线程:1添加值7
写线程:1添加值8
写线程:1添加值9
读取头元素值不删除头元素:写线程:0 值:0
读取头元素值不删除头元素:写线程:0 值:0
读取头元素值不删除头元素:写线程:0 值:0
读取头元素值并删除头元素:写线程:0 值:0
读取头元素值并删除头元素:写线程:0 值:2
读取头元素值并删除头元素:写线程:0 值:3
读取头元素值并删除头元素:写线程:0 值:4
读取头元素值并删除头元素:写线程:0 值:1
读取头元素值并删除头元素:写线程:0 值:6
读取头元素值并删除头元素:写线程:0 值:7
读取头元素值并删除头元素:写线程:0 值:8
读取头元素值并删除头元素:写线程:0 值:9
读取头元素值并删除头元素:写线程:1 值:0
读取头元素值并删除头元素:写线程:1 值:1
读取头元素值并删除头元素:写线程:1 值:2
读取头元素值并删除头元素:写线程:1 值:3
读取头元素值并删除头元素:写线程:1 值:4
读取头元素值并删除头元素:写线程:0 值:5
读取头元素值并删除头元素:写线程:1 值:5
读取头元素值并删除头元素:写线程:1 值:6
读取头元素值并删除头元素:写线程:1 值:7
读取头元素值并删除头元素:写线程:1 值:8
读取头元素值并删除头元素:写线程:1 值:9 Process finished with exit code 0

1.6 注意事项

  如果此队列包含的元素数大于 Integer.MAX_VALUE,则返回 Integer.MAX_VALUE。
  与大多数 collection 不同,size方法不是 一个固定时间操作。由于这些队列的异步特性,确定当前的元素数需要进行一次花费 O(n) 时间的遍历。
  所以在需要判断队列是否为空时,尽量不要用 queue.size()>0,而是用 !queue.isEmpty()

  此队列不允许使用 null 元素

2 PriorityQueue

2.1 简介

  它通过二叉小顶堆实现,可以用一棵完全二叉树表示,形成一个有序队列。

2.2 示例

public static void main(String[] args) {
PriorityQueue q = new PriorityQueue(); q.offer("a");
q.offer("c");
q.offer("f");
q.offer("e");
q.offer("d"); for (int i = 0; i < 5; i++) {
System.out.println(q.poll());
}
}

输出

a
c
d
e
f

3 LinkedBlockingQueue

3.1 简介 

  LinkedBlockingQueue是一个线程安全的阻塞队列,实现了先进先出等特性,是作为生产者消费者的首选,可以指定容量,也可以不指定,不指定的话默认最大是Integer.MAX_VALUE,其中主要用到put和take方法,put方法将一个对象放到队列尾部,在队列满的时候会阻塞直到有队列成员被消费才会添加进去,take方法从head取一个对象,在队列为空的时候会阻塞,直到有队列成员被放进来才会取出

3.2 构造函数

LinkedBlockingQueue() {}
LinkedBlockingQueue(int capacity)
LinkedBlockingQueue(Collection<? extends E> c)

3.3 常用方法

add(anObject):
把anObject添加到BlockingQueue里,添加成功返回true,如果BlockingQueue空间已满则抛出异常。
offer(anObject):
表示如果可能的话,将anObject加到BlockingQueue里,即如果BlockingQueue可以容纳,则返回true,否则返回false。
put(anObject):
把anObject加到BlockingQueue里,如果BlockingQueue没有空间,则调用此方法的线程被阻断直到BlockingQueue里有空间再继续。
poll(time):
获取并移除此队列的头,若不能立即取出,则可以等time参数规定的时间,取不到时返回null。
take():
获取BlockingQueue里排在首位的对象,若BlockingQueue为空,阻断进入等待状态直到BlockingQueue有新的对象被加入为止。
clear():
从队列彻底移除所有元素。
remove()方法直接删除队头的元素
peek()方法直接取出队头的元素,并不删除

3.4 示例

public class DelayQueneTe {

    static class Ta implements Delayed{

        private String name;

        private long time;

        public Ta(String name, long time) {
this.name = name;
this.time = time;
} @Override
public long getDelay(TimeUnit unit) {
return unit.convert(time - System.currentTimeMillis(),unit); //获取延迟时间
} @Override
public int compareTo(Delayed o) {
if(this.getDelay(TimeUnit.MILLISECONDS) < o.getDelay(TimeUnit.MILLISECONDS)){
return -1;
}else if(this.getDelay(TimeUnit.MILLISECONDS) > o.getDelay(TimeUnit.MILLISECONDS)){
return 1;
}else{
return 0;
}
}
}
public static void main(String[] args) throws InterruptedException {
long now = System.currentTimeMillis();
BlockingQueue<Ta> q = new DelayQueue<>();
q.offer(new Ta("t1",now + 1000));
q.offer(new Ta("t2",now + 3000));
q.offer(new Ta("t3",now + 2000));
q.offer(new Ta("t4",now + 5000));
q.offer(new Ta("t5",now + 4000));
for (int i = 0; i < 6; i++) {
System.out.println(q.take().name);
} System.out.println("执行完成"); }
}

输出

线程0拿到了0
线程1拿到了1
线程2拿到了2
线程3拿到了3
线程4拿到了4
线程5拿到了5
线程6拿到了6
线程7拿到了7
线程8拿到了8
线程9拿到了9
睡眠结束
线程10拿到了i
线程11拿到了j

先在队列里加入了10个元素,12个线程去取,发现10个线程被取出后,还有两个线程取不到值二阻塞,知道又加入了两个元素

4 DelayQueue

4.1 简介

  DelayQueue 是一个实现延迟获取元素的无界阻塞的队列,它是BlockingQuene的一种,它是线程安全的。

  其中添加进该队列的元素必须实现Delayed接口(指定延迟时间),而且只有在延迟期满后才能从中提取元素

  可以用作定时任务调度。使用DelayQueue保存当天将会执行的任务和执行时间,一旦从DelayQueue中获取到任务就开始执行

4.2 构造方法

    //从上面属性就可以看出,DelayQueue采用了饿汉模式,调用构造方法即创建了队列实例
public DelayQueue() {} /**
* 创建一个DelayQueue,最初包含给定的Collection实例集合。
* @param c 最初包含的元素集合
*/
public DelayQueue(Collection<? extends E> c) {
this.addAll(c);
}

4.3 put和take

  offer:添加元素

  take:获取元素

4.4 DelayQuene的元素

  使用DelayQueue的话,放入该队列的元素必须实现Delayed接口,实现的接口中有两个参数:延迟时间单位,优先级规则,take方法会根据规则按照优先级执行

4.5 示例

public class DelayQueneTe {

    static class Ta implements Delayed{

        private String name;

        long delayTime; // 延迟时间
long expire; // 过期时间 public Ta(String name, long delayTime) {
this.name = name;
this.delayTime = delayTime;
this.expire = System.currentTimeMillis() + delayTime;
} //剩余时间 = 到期时间 - 当前时间
@Override
public long getDelay(TimeUnit unit) {
return unit.convert(this.expire - System.currentTimeMillis(), TimeUnit.MILLISECONDS);
} //优先级规则:两个任务比较,时间短的优先执行。一个任务到时间了,被取出,就没有优先级的说法,多个任务到执行时间了,没有被取出,现在取出,按这个优先级别取出
@Override
public int compareTo(Delayed o) {
long f = this.getDelay(TimeUnit.MILLISECONDS) - o.getDelay(TimeUnit.MILLISECONDS);
return (int) f;
}
}
public static void main(String[] args) throws InterruptedException {
long now = System.currentTimeMillis();
BlockingQueue<Ta> q = new DelayQueue<>();
q.offer(new Ta("t1", 1000));
q.offer(new Ta("t2", 3000));
q.offer(new Ta("t3", 2000));
q.offer(new Ta("t4", 5000));
q.offer(new Ta("t5", 4000));
for (int i = 0; i < 6; i++) {
System.out.println(q.take().name);
} System.out.println("执行完成"); }
}

执行结果

t1
t3
t2
t5
t4

发现,它是按照传入的时间来执行的

且由于只有5个元素添加了,所以取第六个的时候,一致取不到,就会一直阻塞

5 SynchronousQueue

5.1 简介

  SynchronousQueue是BlockingQueue的一种,所以SynchronousQueue是线程安全的。

  SynchronousQueue 是比较独特的队列,其本身是没有容量大小,比如我放一个数据到队列中,我是不能够立马返回的,我必须等待别人把我放进去的数据消费掉了,才能够返回。

  也就是说SynchronousQueue的每一次insert操作,必须等待其他线性的remove操作。而每一个remove操作也必须等待其他线程的insert操作。

  这种特性和Exchanger有点类似。
  SynchronousQueue 在消息队列技术中间件中被大量使用

  

5.2 示例

5.2.1 一个线程取数据,一个线程放数据,实现了线程间的数据传递

private static void m1() throws InterruptedException {
BlockingQueue queue = new SynchronousQueue(); new Thread(()->{
try {
Object take = queue.take();
System.out.println(take);
} catch (InterruptedException e) {
e.printStackTrace();
} }).start(); new Thread(()->{
try {
queue.put("aaa");
} catch (InterruptedException e) {
e.printStackTrace();
} }).start();  

输出

aaa

5.2.2 SynchronousQueue的每一次添加操作,必须等待其他线性的取出操作,不然会阻塞

private static void m2() throws InterruptedException {
BlockingQueue queue = new SynchronousQueue(); new Thread(()->{
try {
queue.put("aaa"); //若没有人取,会一直阻塞。
System.out.println("xxxx");
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
}

没有输出,阻塞了

5.2.3 SynchronousQueue的每一次取操作,必须等待其他线性的添加操作,不然会阻塞

private static void m3() throws InterruptedException {
BlockingQueue queue = new SynchronousQueue(); new Thread(()->{
try {
Object take = queue.take();
System.out.println(take);
} catch (InterruptedException e) {
e.printStackTrace();
} }).start(); }

没有输出,阻塞了

5.2.4 多个线程先取,多个线程再添加

private static void m4() throws InterruptedException {
BlockingQueue queue = new SynchronousQueue();
new Thread(()->{
try {
Object take = queue.take();
System.out.println(take);
} catch (InterruptedException e) {
e.printStackTrace();
} }).start(); new Thread(()->{
try {
Object take = queue.take();
System.out.println(take);
} catch (InterruptedException e) {
e.printStackTrace();
} }).start(); new Thread(()->{
try {
queue.put("aaa");
} catch (InterruptedException e) {
e.printStackTrace();
} }).start(); new Thread(()->{
try {
queue.put("bbb");
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start(); }

输出

aaa
bbb

5.2.5 多个线程先添加,多个线程再取

 private static void m6() throws InterruptedException {
BlockingQueue queue = new SynchronousQueue(); new Thread(()->{
try {
queue.put("aaa");
} catch (InterruptedException e) {
e.printStackTrace();
} }).start(); new Thread(()->{
try {
queue.put("bbb");
} catch (InterruptedException e) {
e.printStackTrace();
} }).start(); new Thread(()->{
try {
Object take = queue.take();
System.out.println(take);
} catch (InterruptedException e) {
e.printStackTrace();
} }).start(); new Thread(()->{
try {
Object take = queue.take();
System.out.println(take);
} catch (InterruptedException e) {
e.printStackTrace();
} }).start();
}

输出

bbb
aaa

5.2.6 add方法(调用会报错)

private static void m8() throws InterruptedException {
BlockingQueue queue = new SynchronousQueue(); new Thread(()->{
queue.add("aaa");
System.out.println("添加aaa成功"); }).start();
}

输出报错

Exception in thread "Thread-0" java.lang.IllegalStateException: Queue full
at java.util.AbstractQueue.add(AbstractQueue.java:98)
at com.ruoyi.weixin.user.MyTest.Quene.SynchrnousQueneTe.lambda$m8$16(SynchrnousQueneTe.java:211)
at java.lang.Thread.run(Thread.java:748)

6 LinkedTransferQueue

6.1 简介

  LinkedTransferQueue是BlockingQueue的一种,所以SynchronousQueue是线程安全的。

  LinkedTransferQueue是一个由链表结构组成的无界阻塞TransferQueue队列。相对于其他阻塞队列,LinkedTransferQueue多了tryTransfer和transfer方法。可以算是 LinkedBolckingQueue 和 SynchronousQueue 和合体。LinkedTransferQueue是一种无界阻塞队列,底层基于单链表实现,其内部节点分为数据结点、请求结点;基于CAS无锁算法实现。

  它的添加方法提供阻塞的transfer方法,取出方法提供阻塞的take方法

  

6.2 原理

6.2.1 消费者

  LinkedTransferQueue 消费者线程获取取数据时:调用take poll 等方法。

  如果队列不为空,则直接取走数据,若队列为空则消费者线程会生成一个占位虚拟节点(节点元素为null)入队,并等待在这个节点上,后面生产者线程请求添加数据时,会从单向链表的head节点开始遍历,如果发现某个节点是一个取数请求任务类型的节点(即是这个节点的isData为false,item == null),生产者线程就不入队了,直接就将元素填充到该节点(元素传递给它),并唤醒该节点等待的消费者线程,被唤醒的消费者线程取走元素 ;

 6.2.2 生产者

  LinkedTransferQueue 生产者线程传递数据时:调用transfer方法

  当有消费者线程阻塞等待时,调用transfer方法的生产者线程不会将元素存入队列,而是直接将元素传递给消费者,并唤醒阻塞的线程;
  如果调用transfer方法的生产者线程发现没有正在等待的消费者线程,则这个生产者请求创建一个节点,这个节点将会被添加到当前链表的末尾将数据入队,然后会阻塞等待,直到有一个消费者线程来获取该元素。

  LinkedTransferQueue内部链表上的有效节点,要么全部都是由取数请求创建的节点,其isData为false,item属性为null;要么就全部都是由存储请求创建的节点,其isData为true,item属性不为null ,只需要由head开始找到第一个有效节点判定是否可以存储/添加数据,因为只要存在生产者或者消费者在队列时,对应的消费者或者生产者就不会入队列,也就是说二者只有一个会在队列,如果生产者在队列,消费者来取数据就会唤醒它,反之消费者在队列,生产者也会唤醒消费者线程

6.3 示例

6.3.1  一个线程取数据,一个线程放数据,实现了线程间的数据传递

private static void m1() throws InterruptedException {
LinkedTransferQueue queue = new LinkedTransferQueue(); new Thread(()->{
Object take = null;
try {
take = queue.take();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(take);
}).start(); new Thread(()->{
try {
queue.transfer("aaa");
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
}

输出

aaa

6.3.2 每一次添加操作,必须等待其他线性的取出操作,不然会阻塞

private static void m2() throws InterruptedException {
LinkedTransferQueue queue = new LinkedTransferQueue(); new Thread(()->{
try {
queue.transfer("aaa");
System.out.println("xxxxx");
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
}

没有输出,阻塞了

6.3.3 每一次取操作,必须等待其他线性的添加操作,不然会阻塞

private static void m3() throws InterruptedException {
LinkedTransferQueue queue = new LinkedTransferQueue(); new Thread(()->{
try {
Object take = queue.take();
System.out.println(take);
} catch (InterruptedException e) {
e.printStackTrace();
} }).start(); }

没有输出,阻塞了

6.3.4 多个线程先取,多个线程再添加

private static void m4() throws InterruptedException {
LinkedTransferQueue queue = new LinkedTransferQueue(); new Thread(()->{
Object take = null;
try {
take = queue.take();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(take);
}).start(); new Thread(()->{
Object take = null;
try {
take = queue.take();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(take);
}).start(); new Thread(()->{ try {
queue.transfer("aaa");
} catch (InterruptedException e) {
e.printStackTrace();
} }).start();
new Thread(()->{ try {
queue.transfer("bbb");
} catch (InterruptedException e) {
e.printStackTrace();
} }).start(); }

输出

aaa
bbb

6.3.5 多个线程先添加,多个线程再取

private static void m5() throws InterruptedException {
LinkedTransferQueue queue = new LinkedTransferQueue(); new Thread(()->{ try {
queue.transfer("aaa"); //阻塞,等待获取
} catch (InterruptedException e) {
e.printStackTrace();
} }).start();
new Thread(()->{ try {
queue.transfer("bbb");  //阻塞等待获取
} catch (InterruptedException e) {
e.printStackTrace();
} }).start(); new Thread(()->{
Object take = null;
try {
take = queue.take();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(take);
}).start(); new Thread(()->{
Object take = null;
try {
take = queue.take();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(take);
}).start(); }

输出

bbb
aaa

6.3.6 add方法(非阻塞形式,放完就跑)

private static void m6() throws InterruptedException {
LinkedTransferQueue queue = new LinkedTransferQueue(); new Thread(()->{
queue.add("aaa");
System.out.println("添加aaa成功"); }).start();
}

输出

添加aaa成功

6.3.7  当一个线程transfer阻塞时,其它线程还可以通过add添加

private static void m7() throws InterruptedException {
LinkedTransferQueue queue = new LinkedTransferQueue(); new Thread(()->{
try {
queue.transfer("aaa");
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("添加aaa成功"); }).start(); new Thread(()->{
queue.add("bbb");
System.out.println("添加bbb成功"); }).start();
}

输出

添加bbb成功

6.3.8 tryTransfer尝试添加,不会阻塞,没有消费者等待时,添加失败,返回false

private static void m9() throws InterruptedException {
LinkedTransferQueue queue = new LinkedTransferQueue(); new Thread(()->{
try {
boolean aaa = queue.tryTransfer("aaa");
if(aaa){
System.out.println("添加aaa成功");
}else{
System.out.println("添加aaa失败");
}
} catch (Exception e) {
e.printStackTrace();
} }).start(); }

输出

添加aaa失败

6.4 LinkedTransferQueue 和 synchronousqueue 的区别

  LinkedTransferQueue 如果有消费者线程存在,则生产者线程将数据传递到占位节点并唤醒消费者线程。没有消费者线程存在,则阻塞直到消费者取元素,而其他方法不阻塞,

  synchronousqueue 只有阻塞直到消费者获取元素。这个过程中队列不存数据,直接等到消费者来获取时交给了消费者

线程基础知识17 Quene的更多相关文章

  1. Java__线程---基础知识全面实战---坦克大战系列为例

    今天想将自己去年自己编写的坦克大战的代码与大家分享一下,主要面向学习过java但对java运用并不是很熟悉的同学,该编程代码基本上涉及了java基础知识的各个方面,大家可以通过练习该程序对自己的jav ...

  2. java线程基础知识----线程与锁

    我们上一章已经谈到java线程的基础知识,我们学习了Thread的基础知识,今天我们开始学习java线程和锁. 1. 首先我们应该了解一下Object类的一些性质以其方法,首先我们知道Object类的 ...

  3. java线程基础知识----线程基础知识

    不知道从什么时候开始,学习知识变成了一个短期记忆的过程,总是容易忘记自己当初学懂的知识(fuck!),不知道是自己没有经常使用还是当初理解的不够深入.今天准备再对java的线程进行一下系统的学习,希望 ...

  4. Windows核心编程 第六章 线程基础知识 (上)

    第6章 线程的基础知识 理解线程是非常关键的,因为每个进程至少需要一个线程.本章将更加详细地介绍线程的知识.尤其是要讲述进程与线程之间存在多大的差别,它们各自具有什么作用.还要介绍系统如何使用线程内核 ...

  5. React JS 基础知识17条

    1. 基础实例 <!DOCTYPE html> <html> <head> <script src="../build/react.js" ...

  6. Java并发之线程管理(线程基础知识)

    因为书中涵盖的知识点比较全,所以就以书中的目录来学习和记录.当然,学习书中知识的时候自己的思考和实践是最重要的.说到线程,脑子里大概知道是个什么东西,但很多东西都还是懵懵懂懂,这是最可怕的.所以想着细 ...

  7. Java线程基础知识(状态、共享与协作)

    1.基础概念 CPU核心数和线程数的关系 核心数:线程数=1:1 ;使用了超线程技术后---> 1:2 CPU时间片轮转机制 又称RR调度,会导致上下文切换 什么是进程和线程 进程:程序运行资源 ...

  8. java线程基础知识----java daemon线程

    java线程是一个运用很广泛的重点知识,我们很有必要了解java的daemon线程. 1.首先我们必须清楚的认识到java的线程分为两类: 用户线程和daemon线程 A. 用户线程: 用户线程可以简 ...

  9. java并发编程(一)----线程基础知识

    在任何的生产环境中我们都不可逃避并发这个问题,多线程作为并发问题的技术支持让我们不得不去了解.这一块知识就像一个大蛋糕一样等着我们去分享,抱着学习的心态,记录下自己对并发的认识. 1.线程的状态: 线 ...

  10. Java 线程基础知识

    前言 什么是线程?线程,有时被称为轻量进程(Lightweight Process,LWP),是程序执行流的最小单元.一个标准的线程由线程 ID,当前指令指针 (PC),寄存器集合和堆栈组成.另外,线 ...

随机推荐

  1. vue3中使用computed

    演示示例(vant组件库的轮播图): <van-swipe :loop="false" :width="150" class="my-Swipe ...

  2. 基于iNeuOS工业互联网平台的板材实时质检系统

    1.  项目背景 刨花板生产线由于原料.生产工艺等原因,会有一些产品板面出现颤纹.漏砂.胶斑.胶块.大刨花.粉尘斑.板面划痕和油污等缺陷.表面缺陷会降低板材强度.影响板材外观和二次加工,给企业带来经济 ...

  3. MobaXterm/Xshell快速命令宏配置

    背景:使用MobaXterm或者Xshell连接远程服务器时,添加常用的命令宏一键执行,能提高效率节约时间. 使用方法: MobaXterm: 1)左边菜单栏->Macros->Recor ...

  4. 8、将两个字符串s1,s2进行比较,如果s1>s2,则输出一个正数。如果s1 = s2,输出零。如果s1 < s2, 输出一个负数,不用strcmp函数,输出的正数或者负数的绝对值应该是比较两字符串相应字符的ascii码的差值。

    /* 将两个字符串s1,s2进行比较,如果s1>s2,则输出一个正数.如果s1 = s2,输出零.如果s1 < s2, 输出一个负数,不用strcmp函数,输出的正数或者负数的绝对值应该是 ...

  5. SpringBoot使用@Async的总结!

    一些业务场景我们需要使用多线程异步执行任务,加快任务执行速度. 之前有写过一篇文章叫做: 异步编程利器:CompletableFuture 在实际工作中也更加推荐使用CompletableFuture ...

  6. Lakehouse架构指南

    你曾经是否有构建一个开源数据湖来存储数据以进行分析需求? 数据湖包括哪些组件和功能? 不了解 Lakehouse 和 数据仓库 之间的区别? 或者只是想管理数百到数千个文件并拥有更多类似数据库的功能但 ...

  7. 数电第六周周结_by_yc

    时序逻辑电路的设计要点:   ①只有时钟信号和复位信号可以放在敏感列表里:   ②使用非阻塞赋值,即使用"<=";   ③无需对所有分支进行描述,对于未描述的分支,变量将保持 ...

  8. PostgreSQL常用操作合辑:时间日期、系统函数、正则表达式、库表导入导出、元数据查询、自定义函数、常用案例

    〇.参考地址 1.pg官方文档 http://www.postgres.cn/docs/9.6/index.html 2.腾讯云仓pg文档 https://cloud.tencent.com/docu ...

  9. 【离线数仓CDH版本】即席查询工具(Presto、Druid、Kylin)、CDH数仓、Impala查询

    1.即席查询 一.Presto 大数据量.秒级.多数据源的查询引擎[支持各种数据源work的内存级查询] 由coordinator和多个work构成,work对应不同数据源Catalog 特点:基于内 ...

  10. day34-JSON&Ajax02

    JSON&Ajax02 1.Ajax基本介绍 1.1Ajax是什么 AJAX 即"Asynchronous JavaScript And XML"(异步JavaScript ...