JUC面试(八)——阻塞队列

news2024/11/14 0:19:57

阻塞队列

队列,FIFO

BlockingQueue 阻塞队列,排队拥堵,首先它是一个队列,而一个阻塞队列在数据结构中所起的作用大致如下图所示:

在这里插入图片描述

线程1往阻塞队列中添加元素,而线程2从阻塞队列中移除元素

  • 当阻塞队列是空时,从队列中获取元素的操作将会被阻塞
    • 当蛋糕店的柜子空的时候,无法从柜子里面获取蛋糕
  • 当阻塞队列是满时,从队列中添加元素的操作将会被阻塞
    • 当蛋糕店的柜子满的时候,无法继续向柜子里面添加蛋糕了

也就是说 试图从空的阻塞队列中获取元素的线程将会被阻塞,直到其它线程往空的队列插入新的元素

同理,试图往已经满的阻塞队列中添加新元素的线程,直到其它线程往满的队列中移除一个或多个元素,或者完全清空队列后,使队列重新变得空闲起来,并后续新增

为什么要用?

去海底捞吃饭,大厅满了,需要进候厅等待,但是这些等待的客户能够对商家带来利润,因此我们非常欢迎他们阻塞

在多线程领域:所谓的阻塞,在某些清空下会挂起线程(即阻塞),一旦条件满足,被挂起的线程又会自动唤醒

为什么需要BlockingQueue

好处是我们不需要关心什么时候需要阻塞线程,什么时候需要唤醒线程,因为这一切BlockingQueue都帮你一手包办了

在concurrent包发布以前,在多线程环境下,我们每个程序员都必须自己取控制这些细节,尤其还要兼顾效率和线程安全,而这会给我们的程序带来不小的复杂度。

架构

// 你用过List集合类

// ArrayList集合类熟悉么?

// 除了ArrayList和LinkedList 还用过 CopyOnWriteList 和 BlockingQueue

BlockingQueue阻塞队列是属于一个接口,底下有七个实现类

  • ArrayBlockQueue:由数组结构组成的有界阻塞队列
  • LinkedBlockingQueue:由链表结构组成的有界(但是默认大小 Integer.MAX_VALUE,慎用,内存占用打,21亿大小)的阻塞队列
    • 有界,但是界限非常大,相当于无界,可以当成无界
  • PriorityBlockQueue(pri o de t):支持优先级排序的无界阻塞队列
  • DelayQueue:使用优先级队列实现的延迟无界阻塞队列
  • SynchronousQueue:不存储元素的阻塞队列,也即单个元素的队列
    • 生产一个,消费一个,不存储元素,不消费不生产
  • LinkedTransferQueue:由链表结构组成的无界阻塞队列
  • LinkedBlockingDeque:由链表结构组成的双向阻塞队列

这里需要掌握的是:ArrayBlockQueue、LinkedBlockingQueue、SynchronousQueue

BlockingQueue核心方法

在这里插入图片描述

抛出异常当阻塞队列满时:在往队列中add插入元素会抛出 IIIegalStateException:Queue full 当阻塞队列空时:再往队列中remove移除元素,会抛出NoSuchException
特殊性插入方法,成功true,失败false 移除方法:成功返回出队列元素,队列没有就返回空
一直阻塞当阻塞队列满时,生产者继续往队列里put元素,队列会一直阻塞生产线程直到put数据or响应中断退出, 当阻塞队列空时,消费者线程试图从队列里take元素,队列会一直阻塞消费者线程直到队列可用。
超时退出当阻塞队列满时,队里会阻塞生产者线程一定时间,超过限时后生产者线程会退出

抛出异常组

执行add方法,向已经满的ArrayBlockingQueue中添加元素时候,会抛出异常

输出的是队首元素:

System.out.println(blockingQueue.element());
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;

public class Test {

    public static void main(String[] args) {
        // 阻塞队列,需要填入默认值
        BlockingQueue<String> blockingQueue = new ArrayBlockingQueue<>(3);

        System.out.println(blockingQueue.add("a"));
        System.out.println(blockingQueue.add("b"));
        System.out.println(blockingQueue.add("c"));

        System.out.println(blockingQueue.add("XXX"));
    }

}

运行后:

在这里插入图片描述

同时如果我们多取出元素的时候,也会抛出异常,我们假设只存储了3个值,但是取的时候,取了四次

// 阻塞队列,需要填入默认值
BlockingQueue<String> blockingQueue = new ArrayBlockingQueue<>(3);
System.out.println(blockingQueue.add("a"));
System.out.println(blockingQueue.add("b"));
System.out.println(blockingQueue.add("c"));

//队首
System.out.println(blockingQueue.element());

System.out.println(blockingQueue.remove());
System.out.println(blockingQueue.remove());
System.out.println(blockingQueue.remove());
System.out.println(blockingQueue.remove());

那么出现异常:

在这里插入图片描述

布尔类型组

我们使用 offer的方法,添加元素时候,如果阻塞队列满了后,会返回false,否者返回true

同时使用poll方法在取的时候,如果队列已空,那么会返回null

        System.out.println(blockingQueue.offer("a"));
        System.out.println(blockingQueue.offer("b"));
        System.out.println(blockingQueue.offer("c"));

        System.out.println(blockingQueue.offer("XXX"));

        //查看队首数据,没有返回null
        System.out.println(blockingQueue.peek());

        System.out.println(blockingQueue.poll());
        System.out.println(blockingQueue.poll());
        System.out.println(blockingQueue.poll());
        System.out.println(blockingQueue.poll());

运行结果

true
true
true
false
a
a
b
c
null

阻塞队列组

我们使用 put的方法,添加元素时候,如果阻塞队列满了后,添加消息的线程,会一直阻塞,直到队列元素减少,会被清空,才会唤醒

一般在消息中间件,比如RabbitMQ中会使用到,因为需要保证消息百分百不丢失,因此只有让它阻塞

BlockingQueue<String> blockingQueue = new ArrayBlockingQueue<>(3);
blockingQueue.put("a");
blockingQueue.put("b");
blockingQueue.put("c");
System.out.println("================");

blockingQueue.take();
blockingQueue.take();
blockingQueue.take();
blockingQueue.take();

同时使用take取消息的时候,如果内容不存在的时候,也会被阻塞,程序一直不停止,直到再加或取元素完才结束。

在这里插入图片描述

不见不散组

offer( ) , poll()加时间,工作尽量使用这个

使用offer插入的时候,需要指定时间,如果2秒还没有插入成功,那么就放弃插入

BlockingQueue<String> blockingQueue = new ArrayBlockingQueue<>(3);
System.out.println(blockingQueue.offer("a", 2L, TimeUnit.SECONDS));
System.out.println(blockingQueue.offer("b", 2L, TimeUnit.SECONDS));
System.out.println(blockingQueue.offer("c", 2L, TimeUnit.SECONDS));
System.out.println(blockingQueue.offer("d", 2L, TimeUnit.SECONDS));

同时取的时候也进行判断

System.out.println(blockingQueue.poll(2L, TimeUnit.SECONDS));
System.out.println(blockingQueue.poll(2L, TimeUnit.SECONDS));
System.out.println(blockingQueue.poll(2L, TimeUnit.SECONDS));
System.out.println(blockingQueue.poll(2L, TimeUnit.SECONDS));

如果2秒内取不出来,那么就返回null

SynchronousQueue

SynchronousQueue没有容量,与其他BlockingQueue不同,SynchronousQueue是一个不存储的BlockingQueue,每一个put操作必须等待一个take操作,否者不能继续添加元素。不消费就不生产,而且大小只能存一个。

下面我们测试SynchronousQueue添加元素的过程

首先我们创建了两个线程,一个线程用于生产,一个线程用于消费

生产的线程分别put了 A、B、C这三个字段

BlockingQueue<String> blockingQueue = new SynchronousQueue<>();

new Thread(() -> {
    try {       
        System.out.println(Thread.currentThread().getName() + "\t put A ");
        blockingQueue.put("A");
       
        System.out.println(Thread.currentThread().getName() + "\t put B ");
        blockingQueue.put("B");        
        
        System.out.println(Thread.currentThread().getName() + "\t put C ");
        blockingQueue.put("C");        
        
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
}, "t1").start();

消费线程使用take,消费阻塞队列中的内容,并且每次消费前,都等待5秒

        new Thread(() -> {
            try {

                try {
                    TimeUnit.SECONDS.sleep(5);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                blockingQueue.take();
                System.out.println(Thread.currentThread().getName() + "\t take A ");

                try {
                    TimeUnit.SECONDS.sleep(5);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                blockingQueue.take();
                System.out.println(Thread.currentThread().getName() + "\t take B ");

                try {
                    TimeUnit.SECONDS.sleep(5);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                blockingQueue.take();
                System.out.println(Thread.currentThread().getName() + "\t take C ");

            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }, "t2").start();

最后结果输出为:

t1	 put A 

5秒后...

t2	 take A 
t1	 put B 

5秒后...

t2	 take B 
t1	 put C

5秒后...

t2	 take C 

我们从最后的运行结果可以看出,每次t1线程向队列中添加阻塞队列添加元素后,t1输入线程就会等待 t2消费线程,t2消费后,t2处于挂起状态,等待t1在存入,从而周而复始,形成 一存一取的状态

阻塞队列的用处

生产者消费者模式

一个初始值为0的变量,两个线程对其交替操作,一个加1,一个减1,来5轮

关于多线程的操作,我们需要记住下面几句

  • 线程 操作 资源类
  • 判断 干活 通知
  • 防止虚假唤醒机制

我们下面实现一个简单的生产者消费者模式,首先有资源类ShareData

/**
 * 资源类
 */
class ShareData {

    private int number = 0;

    private Lock lock = new ReentrantLock();

    private Condition condition = lock.newCondition();

    public void increment() throws Exception{
        // 同步代码块,加锁
        lock.lock();
        try {
            // 判断
            while(number != 0) {
                // 等待不能生产
                condition.await();
            }

            // 干活
            number++;

            System.out.println(Thread.currentThread().getName() + "\t " + number);

            // 通知 唤醒
            condition.signalAll();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }

    public void decrement() throws Exception{
        // 同步代码块,加锁
        lock.lock();
        try {
            // 判断
            while(number == 0) {
                // 等待不能消费
                condition.await();
            }

            // 干活
            number--;

            System.out.println(Thread.currentThread().getName() + "\t " + number);

            // 通知 唤醒
            condition.signalAll();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }

}

里面有一个number变量,同时提供了increment 和 decrement的方法,分别让number 加1和减1

但是我们在进行判断的时候,为了防止出现虚假唤醒机制,不能使用if来进行判断,而应该使用while

虚假唤醒:当一个条件满足时,很多线程都被唤醒了,但是只有其中部分是有用的唤醒,其它的唤醒都是无用功,比如说买货,如果商品本来没有货物,突然进了一件商品,这是所有的线程都被唤醒了,但是只能一个人买,所以其他人都是假唤醒,获取不到对象的锁

// 判断
while(number != 0) {
    // 等待不能生产
    condition.await();
}

不能使用 if判断,使用if,当线程数>2时将会出错,可能。

// 判断
if(number != 0) {
    // 等待不能生产
    condition.await();
}

完整代码

/**
 * 生产者消费者 传统版
 * 题目:一个初始值为0的变量,两个线程对其交替操作,一个加1,一个减1,来5轮
 * @author: wzq
 * @create: 2020-03-16-21:38
 */
/**
 * 线程 操作 资源类
 * 判断 干活 通知
 * 防止虚假唤醒机制
 */

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * 资源类
 */
class ShareData {

    private int number = 0;

    private Lock lock = new ReentrantLock();

    private Condition condition = lock.newCondition();

    public void increment() throws Exception{
        // 同步代码块,加锁
        lock.lock();
        try {
            // 判断
            while(number != 0) {
                // 等待不能生产
                condition.await();
            }

            // 干活
            number++;

            System.out.println(Thread.currentThread().getName() + "\t " + number);

            // 通知 唤醒
            condition.signalAll();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }

    public void decrement() throws Exception{
        // 同步代码块,加锁
        lock.lock();
        try {
            // 判断
            while(number == 0) {
                // 等待不能消费
                condition.await();
            }

            // 干活
            number--;

            System.out.println(Thread.currentThread().getName() + "\t " + number);

            // 通知 唤醒
            condition.signalAll();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }

}
public class ProdConsumerTraditionDemo {

    public static void main(String[] args) {

        // 高内聚,低耦合    内聚指的是,一个空调,自身带有调节温度高低的方法

        ShareData shareData = new ShareData();

        // t1线程,生产
        new Thread(() -> {
            for (int i = 0; i < 5; i++) {
                try {
                    shareData.increment();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }, "t1").start();

        // t2线程,消费
        new Thread(() -> {
            for (int i = 0; i < 5; i++) {
                try {
                    shareData.decrement();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }, "t2").start();
    }
}

最后运行成功后,我们一个进行生产,一个进行消费

t1	 1
t2	 0
t1	 1
t2	 0
t1	 1
t2	 0
t1	 1
t2	 0
t1	 1
t2	 0

生成者和消费者3.0

多线程不用if,用while

在concurrent包发布以前,在多线程环境下,我们每个程序员都必须自己去控制这些细节,尤其还要兼顾效率和线程安全,则这会给我们的程序带来不小的时间复杂度

现在我们使用新版的阻塞队列版生产者和消费者,使用:volatile、CAS、atomicInteger、BlockQueue、线程交互、原子引用

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * 生产者消费者  阻塞队列版
 * 使用:volatile、CAS、atomicInteger、BlockQueue、线程交互、原子引用
 *
 * @author: wzq
 * @create: 2020-03-17-11:13
 */

class MyResource {
    // 默认开启,进行生产消费
    // 这里用到了volatile是为了保持数据的可见性,也就是当TLAG修改时,要马上通知其它线程进行修改
    private volatile boolean FLAG = true;

    // 使用原子包装类,而不用number++,默认0
    private AtomicInteger atomicInteger = new AtomicInteger();

    // 这里不能为了满足条件,而实例化一个具体的SynchronousBlockingQueue
    BlockingQueue<String> blockingQueue = null;

    // 而应该采用依赖注入里面的,构造注入方法传入
    public MyResource(BlockingQueue<String> blockingQueue) {
        this.blockingQueue = blockingQueue;
        // 查询出传入的class是什么
        System.out.println(blockingQueue.getClass().getName());
    }

    /**
     * 生产
     * @throws Exception
     */
    public void myProd() throws Exception{
        String data = null;
        boolean retValue;
        // 多线程环境的判断,一定要使用while进行,防止出现虚假唤醒
        // 当FLAG为true的时候,开始生产
        while(FLAG) {
            data = atomicInteger.incrementAndGet() + "";

            // 2秒存入1个data
            retValue = blockingQueue.offer(data, 2L, TimeUnit.SECONDS);
            if(retValue) {
                System.out.println(Thread.currentThread().getName() + "\t 插入队列:" + data  + "成功" );
                //FLAG = true;
            } else {
                System.out.println(Thread.currentThread().getName() + "\t 插入队列:" + data  + "失败" );
            }

            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        System.out.println(Thread.currentThread().getName() + "\t 停止生产,表示FLAG=false,生产介绍");
    }

    /**
     * 消费
     * @throws Exception
     */
    public void myConsumer() throws Exception{
        String retValue;
        // 多线程环境的判断,一定要使用while进行,防止出现虚假唤醒
        // 当FLAG为true的时候,开始消费
        while(FLAG) {
            // 2秒存入1个data
            retValue = blockingQueue.poll(2L, TimeUnit.SECONDS);
            if(retValue != null && retValue != "") {
                System.out.println(Thread.currentThread().getName() + "\t 消费队列:" + retValue  + "成功" );
            } else {
                FLAG = false;
                System.out.println(Thread.currentThread().getName() + "\t 消费失败,队列中已为空,退出" );

                // 退出消费队列
                return;
            }
        }
    }

    /**
     * 停止生产的判断
     */
    public void stop() {
        this.FLAG = false;
    }

}
public class ProdConsumerBlockingQueueDemo {

    public static void main(String[] args) {
        // 传入具体的实现类, ArrayBlockingQueue
        MyResource myResource = new MyResource(new ArrayBlockingQueue<String>(10));

        new Thread(() -> {
            System.out.println(Thread.currentThread().getName() + "\t 生产线程启动");
            System.out.println("");
            System.out.println("");
            try {
                myResource.myProd();
                System.out.println("");
                System.out.println("");
            } catch (Exception e) {
                e.printStackTrace();
            }
        }, "prod").start();


        new Thread(() -> {
            System.out.println(Thread.currentThread().getName() + "\t 消费线程启动");

            try {
                myResource.myConsumer();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }, "consumer").start();

        // 5秒后,停止生产和消费
        try {
            TimeUnit.SECONDS.sleep(5);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println("");
        System.out.println("");
        System.out.println("5秒中后,生产和消费线程停止,线程结束");
        myResource.stop();
    }
}

最后运行结果

java.util.concurrent.ArrayBlockingQueue
prod	 生产线程启动


consumer	 消费线程启动
prod	 插入队列:1成功
consumer	 消费队列:1成功
prod	 插入队列:2成功
consumer	 消费队列:2成功
prod	 插入队列:3成功
consumer	 消费队列:3成功
prod	 插入队列:4成功
consumer	 消费队列:4成功
prod	 插入队列:5成功
consumer	 消费队列:5成功


5秒中后,生产和消费线程停止,线程结束
prod	 停止生产,表示FLAG=false,生产介绍

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/177014.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Java基础(程序流程控制)

程序流程控制1..顺序结构程序从上到下逐行执行&#xff0c;中间没有判断和跳转2.分支结构根据条件&#xff0c;选择性执行某段代码有if-else和switch-case两种分支需要注意根据相应的方法&#xff0c;来输入指定类型的值。如果不匹配则会异常&#xff1a;InputMisMatchExceptio…

结构型模式-桥接模式

1.概述 现在有一个需求&#xff0c;需要创建不同的图形&#xff0c;并且每个图形都有可能会有不同的颜色。我们可以利用继承的方式来设计类的关系&#xff1a; 我们可以发现有很多的类&#xff0c;假如我们再增加一个形状或再增加一种颜色&#xff0c;就需要创建更多的类。试…

2.5总线标准

文章目录一、引子二、总线标准1.基本概念2.总线标准&#xff08;1&#xff09;系统总线①ISA②EISA扩展总线③拓展&#xff08;2&#xff09;局部总线①VESA②PCI③AGP④PCI-E&#xff08;3&#xff09;设备总线①RS-232C②SCSI③PCMCIA④USB3.连接硬盘总线标准①IDE②SATA三、…

JAVA 服务内存占用太高

一、问题现象 某天&#xff0c;运维老哥突然找我&#xff1a;“你们的某 JAVA 服务内存占用太高&#xff0c;告警了&#xff01;GC 后也没释放&#xff0c;内存只增不减&#xff0c;是不是内存泄漏了&#xff01;” 然后我赶紧看了下监控&#xff0c;一切正常&#xff0c;距离上…

redis缓存问题引进

1、缓存使用 为了系统性能的提升&#xff0c;我们一般都会将部分数据放入缓存中&#xff0c;加速访问。而 db 承担数据落 盘工作。 哪些数据适合放入缓存&#xff1f;  即时性、数据一致性要求不高的  访问量大且更新频率不高的数据&#xff08;读多&#xff0c;写少&…

BigDecimal BigInteger的使用

1、BigDiCemal 【问题】在项目中&#xff0c;我们进行计算的时候&#xff0c;有时候需要考虑 四舍五入&#xff0c;精度丢失的问题&#xff0c;面对这种问题&#xff0c;我们应该怎么处理&#xff1f; System.out.println(0.20.1);System.out.println(0.3-0.1);System.out.prin…

SEO中社交信号的重要性:Facebook分析

你可能认为 SEO中的社交信号是一些无用的社交账号&#xff0c;但它在搜索引擎优化中占有重要地位。Facebook是目前全球最大的社交媒体平台&#xff0c;它已经成为我们日常生活不可缺少的一部分。如何分析和利用好 Facebook&#xff0c;是我们学习 SEO的重中之重。在接下来的内容…

58 应用服务 hang 住, 导致服务 503 Service Unavailable

前言 这是之前 我们测试环境出现的一个问题 一个项目, 代码调整了之后, 发布到测试环境 之后, 几分钟之后 整个系统访问这个服务 出现了 "503 Service Unavailable", 然后 当时的处理方式为 临时重启服务 但是过了一会儿之后 同样的问题还是会出现, 导致 前端服务…

Functions重要部分

Functions1. Defining Functions2. Looking Up Names in Environments1. Defining Functions 赋值&#xff08;Assignment&#xff09;是一种简单的抽象方式&#xff1a;把值&#xff08;values&#xff09;和名称&#xff08;names&#xff09;联系起来。 定义函数&#xff0…

《从零开始编写一个直播服务器》 C++ 实现一个最简单的RTSP流媒体服务器

流媒体开发系列文章 文章目录流媒体开发系列文章前言一、rtsp流是什么&#xff1f;二、使用步骤1.服务器代码总结前言 在安防行业中&#xff0c;onvif协议与gb协议是两种标准&#xff0c;gb是国内安防行业的标准&#xff0c;onvif是国外的安防行业的标准&#xff0c;其中gb281…

AcWing 1013. 机器分配(分组背包问题与方案记录)

一、题目 二、思路 这道题其实不太容易看出背后的模型。这道题本质上是一个分组背包问题。我们将每一个公司看成一组&#xff0c;而在每一个组内&#xff0c;将不同情况下的盈利状况看作物品的价值&#xff0c;而得到这种利益所需的机器数目看作物品的体积。 因此&#xff0c…

SpringSession笔记

第一章、Session会话管理概述Session和Cookie回顾Session机制由于HTTP是无状态的协议&#xff0c;每次浏览器与服务器的交互过程就是一次对话&#xff0c;对话结束之后服务器不能记住你这个人。下次对话的时候服务端无法识别你是上次的人&#xff0c;所以需要一种机制来记录用户…

Win10 突然蓝屏安全模式进不了,没有别的电脑和装机U盘,怎么把资料临时导出来?

环境: Win 10专业版 DELL 3490 移动硬盘/普通U盘 问题描述: Win10 突然蓝屏安全模式进不了,没有别的电脑和装机U盘,怎么把资料临时导出来?目前可以进入Win RE 疑难-高级选项 解决方案: 1.先插入,移动硬盘或者普通U盘 2.多重启几次,自动修复系统失败画面,点击高…

Linux嵌入式开发——压缩与解压缩

文章目录Linux嵌入式开发——压缩与解压缩一、前期准备二、Linux下的压缩格式三、gzip压缩工具1、gzip压缩文件2、gzip压缩文件夹四、bzip2压缩工具五、tar打包工具tar参数对.tar.bz2进行压缩和解压缩压缩解压缩对.tar.gz进行压缩和解压缩压缩解压缩六、其他格式的压缩和解压缩…

Qt扫盲-网络编程概述

网络编程概述一、Qt网络编程概述二、Qt对Http&#xff0c;FTP应用层协议支持三、TCP通信编程支持四、UDP通信编程支持五、主机信息的获取六、网络代理七、底层管理的支持一、Qt网络编程概述 Qt Network模块提供了允许我们编写TCP/IP客户端和服务器的类。它提供了低级类来完成基…

【Java寒假打卡】JavaWeb-Session

【Java寒假打卡】JavaWeb-Session概述常用的方法HttpSession的获取HttpSession的使用概述 常用的方法 HttpSession的获取 HttpSession的使用 在第一个Servlet中获取请求的用户名获取HttpSession对象将用户名设置到共享数据中在第二个Servlet中获取HttpSession对象获取共享数据用…

java8新特性

接口中默认方法修饰为普通方法&#xff0c;实现接口时不需要重写方法Lambda表达式Stream流并行流原理校验当前对象是否为null当前对象为null,设置默认值 接口中默认方法修饰为普通方法Lambda表达式使用Lambda表达式 依赖于函数接口 在接口中只能够允许有一个抽象方法在函数接…

SpringCloud笔记

2023年最新笔记&#xff0c;全文约 3 万字&#xff0c;蕴含 Spring Cloud 常用组件 Nacos、OpenFeign、Seata、Sentinel 等 〇、简介 什么是Spring Cloud&#xff1f; ​ Spring Cloud是一系列框架的有序集合&#xff0c;是一种基于微服务的分布式架构技术。它利用 Spring Boot…

HBuilder的安装与试用

准备把前端框架Layui仔细学习一遍&#xff08;虽然Layui已经过了最流行的时候&#xff0c;但是很多项目都在用它&#xff09;&#xff0c;在B站找了一套《Layui框架精讲全套视频教程》&#xff0c;视频作者实操Layui时用的工具很方便&#xff0c;从弹幕中看到说用的是HBuilder&…

Springcloud 笔记

微服务架构微服务架构是一种架构模式&#xff0c;它体长将单一应用程序划分成一组小的服务&#xff0c;服务之间相互协调&#xff0c;互相配合&#xff0c;为用户提供最终价值。每个服务运行在其独立的进程中&#xff0c;服务与服务之间采用轻量级的通信机制**(如HTTP)互相协作…