kafka(五)spring-kafka(2)详解与demo

news2024/11/28 8:30:27

一、简单的收发消息demo

父工程pom:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.example</groupId>
    <artifactId>kafka-demo</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>pom</packaging>
    <modules>
        <module>producer</module>
        <module>consumer-1</module>
        <module>consumer-2</module>
    </modules>

    <!-- springBoot -->
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.1.4.RELEASE</version>
    </parent>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <!--kafka-->
        <dependency>
            <groupId>org.apache.kafka</groupId>
            <artifactId>kafka-clients</artifactId>
<!--            <version>3.0.0</version>-->
        </dependency>

        <dependency>
            <groupId>org.springframework.kafka</groupId>
            <artifactId>spring-kafka</artifactId>
        </dependency>

        <!--lombok-->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>

        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.78</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

</project>
1、生产者

1.1、配置文件
spring.kafka.bootstrap-servers=localhost:9092
spring.kafka.producer.key-serializer=org.apache.kafka.common.serialization.StringSerializer
spring.kafka.producer.value-serializer=org.apache.kafka.common.serialization.StringSerializer

user.topic = userTest
school.topic = schoolTest
1.2、dto
package com.example.dto;

import lombok.Builder;
import lombok.Data;

@Data
@Builder
public class SchoolDTO {
    private String schoolId;
    private String schoolName;
}
package com.example.dto;

import lombok.Builder;
import lombok.Data;

@Data
@Builder
public class UserDTO {
    private String userId;
    private String userName;
    private Integer age;
}
 1.3、service
package com.example.service.impl;

import com.alibaba.fastjson.JSON;
import com.example.dto.SchoolDTO;
import com.example.dto.UserDTO;
import com.example.service.SchoolService;
import lombok.extern.slf4j.Slf4j;
import org.apache.kafka.clients.producer.KafkaProducer;
import org.apache.kafka.clients.producer.ProducerRecord;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.kafka.core.KafkaTemplate;
import org.springframework.stereotype.Service;

@Service("schoolService")
@Slf4j
public class SchoolServiceImpl implements SchoolService {

    @Autowired
    private KafkaTemplate<String, String> kafkaTemplate;

    @Value("${school.topic}")
    private String schoolTopic;

    @Override
    public void sendSchoolMsg(SchoolDTO schoolDTO) {
        String msg = JSON.toJSONString(schoolDTO);
        ProducerRecord producerRecord = new ProducerRecord(schoolTopic,msg);
        kafkaTemplate.send(producerRecord);
        log.info("school消息发送成功");
    }
}
package com.example.service.impl;

import com.alibaba.fastjson.JSON;
import com.example.dto.UserDTO;
import com.example.service.UserService;
import lombok.extern.slf4j.Slf4j;
import org.apache.kafka.clients.producer.KafkaProducer;
import org.apache.kafka.clients.producer.ProducerRecord;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.kafka.core.KafkaTemplate;
import org.springframework.stereotype.Service;


@Service("userService")
@Slf4j
public class UserServiceImpl implements UserService {

    @Autowired
    private KafkaTemplate<String, String> kafkaTemplate;

    @Value("${user.topic}")
    private String userTopic;

    @Override
    public void sendUserMsg(UserDTO userDTO) {
        String msg = JSON.toJSONString(userDTO);
        ProducerRecord producerRecord = new ProducerRecord(userTopic,msg);
        kafkaTemplate.send(producerRecord);
        log.info("user消息发送成功");
    }
}
 1.4、启动类
package com.example;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class ProducerApplication {
    public static void main(String[] args) {
        SpringApplication.run(ProducerApplication.class, args);
    }
}
2、消费者

2.1、配置文件
spring.kafka.bootstrap-servers=localhost:9092
spring.kafka.consumer.key-deserializer=org.apache.kafka.common.serialization.StringDeserializer
spring.kafka.consumer.value-deserializer=org.apache.kafka.common.serialization.StringDeserializer
spring.kafka.consumer.auto-offset-reset=earliest
spring.kafka.consumer.enable-auto-commit = true 


user.topic = userTest
user.group.id = user-group-1

school.topic = schoolTest
school.group.id = school-group-1

server.port = 2222
2.2、监听
package com.example.listen;

import lombok.extern.slf4j.Slf4j;
import org.apache.kafka.clients.consumer.ConsumerConfig;
import org.apache.kafka.clients.consumer.ConsumerRecord;
import org.apache.kafka.clients.consumer.ConsumerRecords;
import org.apache.kafka.clients.consumer.KafkaConsumer;
import org.apache.kafka.common.serialization.StringDeserializer;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.kafka.annotation.KafkaListener;
import org.springframework.kafka.support.Acknowledgment;
import org.springframework.kafka.support.KafkaHeaders;
import org.springframework.messaging.handler.annotation.Header;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import java.time.Duration;
import java.util.ArrayList;
import java.util.Optional;
import java.util.Properties;

@Component
@Slf4j
public class SchoolConsumer {

    @KafkaListener(topics = "${school.topic}", groupId = "${school.group.id}")
    public void consumer(ConsumerRecord<?, ?> record) {
        try {
            Object message = record.value();
            if (message != null) {
                String msg = String.valueOf(message);
                log.info("接收到:msg={},topic:{},partition={},offset={}",msg,record.topic(),record.partition(),record.offset());

            }
        } catch (Exception e) {
            log.error("topic:{},is consumed error:{}", record.topic(), e.getMessage());
        } finally {
            //ack.acknowledge();
        }
    }
}
package com.example.listen;

import lombok.extern.slf4j.Slf4j;
import org.apache.kafka.clients.consumer.ConsumerRecord;
import org.springframework.kafka.annotation.KafkaListener;
import org.springframework.stereotype.Component;

@Component
@Slf4j
public class UserConsumer {

    @KafkaListener(topics = "${user.topic}", groupId = "${user.group.id}")
    public void consumer(ConsumerRecord<?, ?> record) {
        try {
            Object message = record.value();
            if (message != null) {
                String msg = String.valueOf(message);
                log.info("接收到:msg={},topic:{},partition={},offset={}",msg,record.topic(),record.partition(),record.offset());

            }
        } catch (Exception e) {
            log.error("topic:{},is consumed error:{}", record.topic(), e.getMessage());
        } finally {
            //ack.acknowledge();
        }
    }
}

不指定group.id会报错,这也验证了kafka consumer必须要有group id。如写:

@KafkaListener(topics = "${user.topic}")
public void consumer(ConsumerRecord<?, ?> record) 启动报错:

Caused by: java.lang.IllegalStateException: No group.id found in consumer config, container properties, or @KafkaListener annotation; a group.id is required when group management is used.
    at org.springframework.util.Assert.state(Assert.java:73) ~[spring-core-5.1.6.RELEASE.jar:5.1.6.RELEASE]
 

2.3、启动类 
package com.example;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.kafka.annotation.EnableKafka;

@SpringBootApplication
//@EnableKafka
public class Consumer1Application {
    public static void main(String[] args) {
        SpringApplication.run(Consumer1Application.class, args);
    }
}
3、测试

启动消费者:

生产者这里通过单元测试来发送消息:

package com.demo.kafka;

import com.example.ProducerApplication;
import com.example.dto.SchoolDTO;
import com.example.dto.UserDTO;
import com.example.service.SchoolService;
import com.example.service.UserService;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

@SpringBootTest(classes = {ProducerApplication.class}, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@RunWith(SpringRunner.class)
public class Test {

    @Autowired
    private SchoolService schoolService;

    @Autowired
    private UserService userService;

    @org.junit.Test
    public void sendUserMsg(){
        UserDTO userDTO = UserDTO.builder()
                .userId("id-1")
                .age(18)
                .userName("zs")
                .build();
        userService.sendUserMsg(userDTO);
    }

    @org.junit.Test
    public void sendSchoolMsg(){
        SchoolDTO schoolDTO = SchoolDTO.builder()
                .schoolId("schoolId-1")
                .schoolName("mid school")
                .build();
        schoolService.sendSchoolMsg(schoolDTO);
    }

}

运行单测,观察消费者输出:

修改参数再次运行,观察到消费者都可以正常监听: 

2024-06-22 17:09:06.383  INFO 76104 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat started on port(s): 2222 (http) with context path ''
2024-06-22 17:09:06.390  INFO 76104 --- [ntainer#1-0-C-1] org.apache.kafka.clients.Metadata        : Cluster ID: ZdpIAHTjS9GhJlvPP8n0Rw
2024-06-22 17:09:06.392  INFO 76104 --- [ntainer#1-0-C-1] o.a.k.c.c.internals.AbstractCoordinator  : [Consumer clientId=consumer-4, groupId=user-group-1] Discovered group coordinator localhost:9092 (id: 2147483647 rack: null)
2024-06-22 17:09:06.393  INFO 76104 --- [ntainer#1-0-C-1] o.a.k.c.c.internals.ConsumerCoordinator  : [Consumer clientId=consumer-4, groupId=user-group-1] Revoking previously assigned partitions []
2024-06-22 17:09:06.394  INFO 76104 --- [ntainer#1-0-C-1] o.s.k.l.KafkaMessageListenerContainer    : partitions revoked: []
2024-06-22 17:09:06.394  INFO 76104 --- [ntainer#1-0-C-1] o.a.k.c.c.internals.AbstractCoordinator  : [Consumer clientId=consumer-4, groupId=user-group-1] (Re-)joining group
2024-06-22 17:09:06.401  INFO 76104 --- [ntainer#0-0-C-1] o.a.k.c.c.internals.AbstractCoordinator  : [Consumer clientId=consumer-2, groupId=school-group-1] Successfully joined group with generation 11
2024-06-22 17:09:06.403  INFO 76104 --- [ntainer#0-0-C-1] o.a.k.c.c.internals.ConsumerCoordinator  : [Consumer clientId=consumer-2, groupId=school-group-1] Setting newly assigned partitions [schoolTest-0]
2024-06-22 17:09:06.403  INFO 76104 --- [           main] com.example.Consumer1Application         : Started Consumer1Application in 8.606 seconds (JVM running for 9.621)
2024-06-22 17:09:06.413  INFO 76104 --- [ntainer#0-0-C-1] o.s.k.l.KafkaMessageListenerContainer    : partitions assigned: [schoolTest-0]
2024-06-22 17:09:06.491  INFO 76104 --- [ntainer#1-0-C-1] o.a.k.c.c.internals.AbstractCoordinator  : [Consumer clientId=consumer-4, groupId=user-group-1] Successfully joined group with generation 3
2024-06-22 17:09:06.493  INFO 76104 --- [ntainer#1-0-C-1] o.a.k.c.c.internals.ConsumerCoordinator  : [Consumer clientId=consumer-4, groupId=user-group-1] Setting newly assigned partitions [userTest-0]
2024-06-22 17:09:06.611  INFO 76104 --- [ntainer#1-0-C-1] o.s.k.l.KafkaMessageListenerContainer    : partitions assigned: [userTest-0]
2024-06-22 17:16:29.775  INFO 76104 --- [ntainer#1-0-C-1] com.example.listen.UserConsumer          : 接收到:msg={"age":18,"userId":"id-1","userName":"zs"},topic:userTest,partition=0,offset=4
2024-06-22 17:16:48.157  INFO 76104 --- [ntainer#0-0-C-1] com.example.listen.SchoolConsumer        : 接收到:msg={"schoolId":"schoolId-1","schoolName":"mid school"},topic:schoolTest,partition=0,offset=1
2024-06-22 17:17:39.458  INFO 76104 --- [ntainer#1-0-C-1] com.example.listen.UserConsumer          : 接收到:msg={"age":20,"userId":"id-2","userName":"ls"},topic:userTest,partition=0,offset=5
2024-06-22 17:17:59.474  INFO 76104 --- [ntainer#0-0-C-1] com.example.listen.SchoolConsumer        : 接收到:msg={"schoolId":"schoolId-2","schoolName":"primary school"},topic:schoolTest,partition=0,offset=2
4、多个消费者
4.1、同一个groupId

将consumer-1的代码copy到consumer-2,注意端口号修改成不一样的3333,并启动,

spring.kafka.bootstrap-servers=localhost:9092
spring.kafka.consumer.key-deserializer=org.apache.kafka.common.serialization.StringDeserializer
spring.kafka.consumer.value-deserializer=org.apache.kafka.common.serialization.StringDeserializer
spring.kafka.consumer.auto-offset-reset=earliest
spring.kafka.consumer.enable-auto-commit = true 


user.topic = userTest
user.group.id = user-group-1

school.topic = schoolTest
school.group.id = school-group-1

server.port = 3333
2024-06-22 17:23:59.524  INFO 78096 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat started on port(s): 3333 (http) with context path ''
2024-06-22 17:23:59.531  INFO 78096 --- [           main] example.Consumer2Application             : Started Consumer2Application in 7.534 seconds (JVM running for 8.506)
2024-06-22 17:24:00.021  INFO 78096 --- [ntainer#0-0-C-1] o.a.k.c.c.internals.AbstractCoordinator  : [Consumer clientId=consumer-2, groupId=school-group-1] Successfully joined group with generation 12
2024-06-22 17:24:00.024  INFO 78096 --- [ntainer#0-0-C-1] o.a.k.c.c.internals.ConsumerCoordinator  : [Consumer clientId=consumer-2, groupId=school-group-1] Setting newly assigned partitions []
2024-06-22 17:24:00.025  INFO 78096 --- [ntainer#0-0-C-1] o.s.k.l.KafkaMessageListenerContainer    : partitions assigned: []
2024-06-22 17:24:00.028  INFO 78096 --- [ntainer#1-0-C-1] o.a.k.c.c.internals.AbstractCoordinator  : [Consumer clientId=consumer-4, groupId=user-group-1] Successfully joined group with generation 4
2024-06-22 17:24:00.028  INFO 78096 --- [ntainer#1-0-C-1] o.a.k.c.c.internals.ConsumerCoordinator  : [Consumer clientId=consumer-4, groupId=user-group-1] Setting newly assigned partitions []
2024-06-22 17:24:00.029  INFO 78096 --- [ntainer#1-0-C-1] o.s.k.l.KafkaMessageListenerContainer    : partitions assigned: []

再执行次生产者的Test,观察两个消费者:

 可以看到consumer-1接收到了,而consumer-2没有接收到。

再次执行,结果相同。school也是同样的结果。

验证了:同一个topic下的某个分区只能被消费者组中的一个消费者消费。

4.2、不同group

现修改cosumer-2中groupId并重启

user.group.id = user-group-2
school.group.id = school-group-2

启动后自动接收了之前发送的所有消息(因为这是一个新的消费者组):

再次发送新的消息:

可以看到consumer-1和2同时都接收到了:

 

验证了:同一个topic可以被不同的消费者组消费。

二、生产者分区partition

先观察上面步骤产生的数据文件:

上面只有一个patition, 所有两个topic各自只有一个数据目录。现将userTest这个topic分成多个partition,结合四种分区策略看下:

1、指定分区
2、轮询
3、key哈希分区策略
4、自定义分区策略(即自定义Partitioner)

三、消费者分配策略

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

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

相关文章

外包IT运维解决方案

随着企业信息化进程的不断深入&#xff0c;IT系统的复杂性和重要性日益增加。高效的IT运维服务对于保证业务连续性、提升企业竞争力至关重要。外包IT运维解决方案通过专业的服务和技术支持&#xff0c;帮助企业降低运维成本、提高运维效率和服务质量。 本文结合《外包IT运维解…

Go语言的诞生背景

人不走空 &#x1f308;个人主页&#xff1a;人不走空 &#x1f496;系列专栏&#xff1a;算法专题 ⏰诗词歌赋&#xff1a;斯是陋室&#xff0c;惟吾德馨 目录 &#x1f308;个人主页&#xff1a;人不走空 &#x1f496;系列专栏&#xff1a;算法专题 ⏰诗词歌…

数字营销新玩法:拓新与裂变的完美结合

在当今这个飞速发展的数字化时代&#xff0c;数字营销已经成为了企业发展中至关重要的一环。拓新&#xff0c;简单来说就是不断去开拓新的客户群体&#xff0c;让更多的人了解并接触到我们的产品或服务。要做到这一点&#xff0c;那可得充分利用各种线上渠道。像热闹非凡的社交…

设计模式(七)创建者模式之建造者模式

这里写目录标题 概述需求需求类图BikeBuilderMobikeBuilderOfoBuilderDirectorClientClient优缺点使用场景 模式扩展ComputerClient创建者模式对比工厂方法模式VS建造者模式抽象工厂模式VS建造者模式 总结 概述 建造者模式又叫生成器模式&#xff0c;是一种对象构建模式。它可…

threejs视频融合 webgl

threejs三维视频融合 let objList []; const clock new THREE.Clock(); const container document.getElementById( container );const stats new Stats(); container.appendChild( stats.dom );const renderer new THREE.WebGLRenderer( { antialias: true } ); rendere…

Day1:二分查找704 移除元素27

题目链接704. 二分查找 - 力扣&#xff08;LeetCode&#xff09; int search(vector<int>& nums, int target) {int left 0;int right nums.size() - 1;int mid (right - left) / 2;while (left < right){if (target nums[mid]){return mid;}else if (target …

代码阅读器--Understand

代码阅读器--Understand 1 介绍2 安装步骤2.1 下载连接2.2 正常安装&#xff0c;设置自己的安装路径2.3 修改 understand.exe&#xff0c;搜索"areYouThere" &#xff0c; 用"IamNotHere!" 替代2.4 字节序替换 3 使用参考 1 介绍 Understand 的强大不言而…

用于制作耳机壳的UV树脂耳机壳UV胶价格高不高?

用于制作耳机壳的UV树脂耳机壳UV胶价格高不高&#xff1f; 制作耳机壳的UV树脂价格相对于一些其他材料可能会略高&#xff0c;但具体的价格取决于多个因素&#xff0c;如品牌、型号、质量等。一些高端的UV树脂品牌和型号可能会价格较高&#xff0c;但它们也通常具有更好的性能…

无法打开微软商店

今天给大家讲一下我在使用win11系统时遇到的一些问题&#xff0c;希望对出现类似情况的人有所帮助。 首先就是微软商店打不开的问题。相信许多windows系统的用户都会碰到此问题。我在打开时会出 现一直转圈的界面&#xff0c;在网上找了一些方法&#xff0c;但都没什么用处。…

提前还贷有“坑”?房产抵押经营贷避坑指南(十六大常见问题)

一、自己去银行还是找专人办理&#xff1f; 贷款这事儿&#xff0c;说起来容易&#xff0c;办起来可不简单。银行的大门敞开&#xff0c;但门槛却不低。很多人觉得自己资质不错&#xff0c;结果一申请才发现&#xff0c;条件这儿那儿都不符合&#xff0c;最后搞得心力交瘁&…

聚类算法(2)--- ISODATA算法

本篇文章是博主在人工智能等领域学习时&#xff0c;用于个人学习、研究或者欣赏使用&#xff0c;并基于博主对人工智能等领域的一些理解而记录的学习摘录和笔记&#xff0c;若有不当和侵权之处&#xff0c;指出后将会立即改正&#xff0c;还望谅解。文章分类在AI学习笔记&#…

【金】02Y90-60 大数据-HivetoMysQL

1、安装 Java 程序&#xff08;jdk&#xff09; 2、添加以下JAR包 3、确认配置成自己的数据库 ....

程序员为什么不能一次性写好,需要一直改Bug?

程序员在编写代码时不能一次性写好&#xff0c;而是需要不断修改Bug&#xff0c;这主要是由几个因素导致的&#xff1a; 复杂性&#xff1a;软件开发是一个高度复杂的过程&#xff0c;涉及到多个模块、功能、逻辑和数据的交互。即使是最有经验的程序员&#xff0c;也很难一次性…

[最新教程]Claude Sonnet 3.5注册方法详细步骤分享,新手小白收藏,文末免费送已注册的Claude账号

一.Claude sonnet 3.5大模型面世 6月21日&#xff0c;被称为“OpenAI 最强竞对”的大模型公司 Anthropic 发布了 Claude 3.5 系列模型中的第一个版本——Claude 3.5 Sonnet。 Anthropic 在官方博客中表示&#xff0c;Claude 3.5 Sonnet 提高了智能化的行业标准&#xff0c;在…

24年安克创新社招入职自适应能力cata测评真题分享北森测评高频题库

第一部分&#xff1a;安克创新自适应能力cata测评 感谢您关注安克创新社会招聘&#xff0c;期待与您一起弘扬中国智造之美。 为对您做出全面的评估&#xff0c;现诚邀您参加我们的在线测评。 测评名称&#xff1a;社招-安克创新自适应能力cata测评 第二部分&#xff1a;安克…

程序猿大战Python——面向对象——继承基础

定义类的几种语法 目标&#xff1a;了解定义类的标准语法。 我们知道&#xff0c;可以使用class关键字定义类。 在类的使用中&#xff0c;定义方式有三种&#xff1a; &#xff08;1&#xff09;【类名】 &#xff08;2&#xff09;【类名()】 &#xff08;3&#xff09;【…

【C++实验】多项式加减

题目&#xff1a;一元多项式运算 基本要求&#xff1a; &#xff08;1&#xff09; 输入并建立多项式; &#xff08;2&#xff09; 输出多项式; &#xff08;3&#xff09; 多项式加法 &#xff08;4&#xff09; 多项式减法。 测试数据&#xff1a; 代码展示&#xff1a; #i…

MarkDown基础

一、MarkDown标题 1.使用和-表示一级标题 2.使用#、##、###、####、######、######表示一级至六级标题 一级标题 二级标题 一级标题 二级标题 三级标题 四级标题 五级标题 六级标题 二、MarkDown标题 1.Markdown 段落没有特殊的格式&#xff0c;直接编写文字就好&#xff0c;…

全网最全!25届最近5年上海理工大学自动化考研院校分析

上海理工大学 目录 一、学校学院专业简介 二、考试科目指定教材 三、近5年考研分数情况 四、近5年招生录取情况 五、最新一年分数段图表 六、历年真题PDF 七、初试大纲复试大纲 八、学费&奖学金&就业方向 一、学校学院专业简介 二、考试科目指定教材 1、考试…

【Linux系统】多线程

本篇博客继上一篇《线程与线程控制》&#xff0c;又整理了多线程相关的线程安全问题、互斥与锁、同步与条件变量、生产消费模型、线程池等内容&#xff0c;旨在让读者更加深刻地理解线程和初步掌握多线程编程。&#xff08;欲知线程的相关概念、线程控制的相关接口等&#xff0…