学习大数据之JDBC(使用JAVA语句进行SQL操作)(2)

文章目录

    • PreparedStatement预处理对象
    • sql注入的问题以解决方法(预处理对象)
    • 使用预处理对象(PreparedStatement)实现操作
    • 使用预处理对象(PreparedStatement)实现查询操作
    • 使用预处理对象(PreparedStatement)实现插入,删除,修改操作
  • 改造JDBC工具类——结合properties文件
  • PrepareStatement预处理对象
    • MySQL批量添加数据
  • 连接池
    • 连接池之c3p0(拓展)
    • 连接池之Druid(德鲁伊)

PreparedStatement预处理对象

在这里插入图片描述

sql注入的问题以解决方法(预处理对象)

import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Scanner;

public class UserLogin {
    public static void main(String[] args) throws SQLException {
        //创建Scanner对象
        final Scanner scanner = new Scanner(System.in);
        //调用方法键盘录入要登录的用户名和密码
        System.out.println("请你输入用户名");
        String username = scanner.next();
        System.out.println("请你输入密码");
        String password = scanner.next();
        //获取连接
        Connection conn = JDBCUtils.getConn();
        //获取执行平台
        Statement statement = conn.createStatement();
        //准备sql语句
        //String sql = "select * from user where username = " + username + "and password =" + password;
        String sql = "select * from user where username = '"+username+"' and password = '"+password+"'";
        //执行sql语句
        ResultSet resultSet = statement.executeQuery(sql);
        if (resultSet.next()){
            System.out.println("登录成功");
        }else{
            System.out.println("登录失败");
        }
        JDBCUtils.close(conn,statement,resultSet);
    }
}

使用预处理对象(PreparedStatement)实现操作

1.概述:PreparedStatement接口,继承 Statement 接口
2.获取:Connection中的方法
PrepareStatemet prepareStatement(String sql) 获取执行平台
3.执行sql方法:
int executeUpdate()
ResultSet executeQuery()
4.注意:
PreparedStatement支持在sql语句中写?(占位符)
比如:select * from user where username = ? and passwoed = ?
5.为?赋值:
void setObject(int parameterIndex,Object x)
parameterIndex:代表的是第几个?
x 代表的是给?赋的值

使用预处理对象(PreparedStatement)实现查询操作

import java.sql.*;
import java.util.Scanner;

public class UserLogin_PS {
    public static void main(String[] args) throws SQLException {
        //创建Scanner对象
        final Scanner scanner = new Scanner(System.in);
        //调用方法键盘录入要登录的用户名和密码
        System.out.println("请你输入用户名");
        String username = scanner.next();
        System.out.println("请你输入密码");
        String password = scanner.next();
        //获取连接
        Connection conn = JDBCUtils.getConn();
        //准备sql语句
        //String sql = "select * from user where username = " + username + "and password =" + password;
        String sql = "select * from user where username = ? and password = ? ";
        //获取执行平台
        //Statement statement = conn.createStatement();
        PreparedStatement preparedStatement = conn.prepareStatement(sql);
        //执行sql语句
        preparedStatement.setObject(1,username);
        preparedStatement.setObject(2,password);
        ResultSet resultSet = preparedStatement.executeQuery();
        if (resultSet.next()){
            System.out.println("登录成功");
        }else{
            System.out.println("登录失败");
        }
        JDBCUtils.close(conn,preparedStatement,resultSet);
    }

使用预处理对象(PreparedStatement)实现插入,删除,修改操作

public class Demo01PreparedS {
    @Test
    public void insert() throws SQLException {
        //获取连接
        Connection conn = JDBCUtils.getConn();
        //准备sql
        String sql = "insert into `user` values(?,?,?)";
        //获取连接
        PreparedStatement preparedStatement = conn.prepareStatement(sql);
        preparedStatement.setObject(1,null);
        preparedStatement.setObject(2,"张大彪");
        preparedStatement.setObject(3,"8888");
        preparedStatement.executeUpdate();
        JDBCUtils.close(conn,preparedStatement,null);
    }
    @Test
    public void delete() throws SQLException {
        //获取连接
        Connection conn = JDBCUtils.getConn();
        //写sql
        String sql = "delete from `user` where uid = ?";
        PreparedStatement preparedStatement = conn.prepareStatement(sql);
        preparedStatement.setObject(1,6);
        preparedStatement.executeUpdate();
    }
    @Test
    public void update() throws SQLException {
        //获取连接
        Connection conn = JDBCUtils.getConn();
        //写sql
        String sql = "update `user` set username = ? where uid = ?";
        PreparedStatement preparedStatement = conn.prepareStatement(sql);
        preparedStatement.setObject(1,"楚云飞");
        preparedStatement.setObject(2,7);
        preparedStatement.executeUpdate();
    }
}

改造JDBC工具类——结合properties文件

编写第二种工具类

public class JDBCUtils_Two {
    private static String url = null;
    private static String username = null;
    private static String password = null;
    /*
    * 注册驱动,数据库url,用户名,密码
    * 这四大参数应该上来就先初始化
    * 而且只需要初始化一次即可
    * 所以我们需要放在静态代码中
    * */
    static{
        Properties properties = new Properties();
        try {
            FileInputStream fileInputStream = new FileInputStream("D:\\untitled7\\JDBC_DAY04\\src\\JDBC_EXC\\properities.txt");
            properties.load(fileInputStream);
            Class.forName(properties.getProperty("driverClass"));
            url = properties.getProperty("url");
            username = properties.getProperty("username");
            password = properties.getProperty("password");
        } catch (IOException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
    //获取连接
    public static Connection getConn() throws SQLException, ClassNotFoundException {
        Connection conn = null;
        conn = DriverManager.getConnection(url,username,password);
        return conn;
    }
    //关闭资源
    public static void close(Connection conn, Statement st, ResultSet rs){
        if (rs!= null){
            try {
                rs.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        if (st!= null){
            try {
                st.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        if (conn!= null){
            try {
                conn.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }
}

进行测试

public class Demo02PrepareS {
    public static void main(String[] args) throws SQLException, ClassNotFoundException {
        //获取连接
        Connection conn = JDBCUtils_Two.getConn();
        //准备sql
        String sql = "insert into user values(?,?,?);";
        //获取prepareStatement对象
        PreparedStatement preparedStatement = conn.prepareStatement(sql);
        //给?赋值
        preparedStatement.setObject(1,null);
        preparedStatement.setObject(2,"李云龙");
        preparedStatement.setObject(3,"3333");
        //执行sql
        preparedStatement.executeUpdate();
        //关闭资源
        JDBCUtils_Two.close(conn,preparedStatement,null);
    }
}

PrepareStatement预处理对象

建立一个新表

CREATE TABLE category(
	cid INT PRIMARY KEY AUTO_INCREMENT,
	cname VARCHAR(20)
);

MySQL批量添加数据

添加properties文件

driverClass=com.mysql.cj.jdbc.Driver
username=root
password=12345678
url=jdbc:mysql://192.168.10.100:3306/bigdata?rewriteBatchedStatements=true?useUnicode=true&characterEncoding=utf8

1.在设置完所有要添加的参数,调用PreparedStatement中的addBatch(),将sql语句添加到PrepareStatement中
2.调用preparedstatement中的executeBatch()方法批量处理sql语句

public class JDBCUtils {
    private static String url = null;
    private static String username = null;
    private static String password = null;
    /*
    * 注册驱动,数据库url,用户名,密码
    * 这四大参数应该上来就先初始化
    * 而且只需要初始化一次即可
    * 所以我们需要放在静态代码中
    * */
    static{
        Properties properties = new Properties();
        try {
            FileInputStream fileInputStream = new FileInputStream("D:\\untitled7\\JDBC_DAY04\\src\\JDBC_EXC\\properities.txt");
            properties.load(fileInputStream);
            Class.forName(properties.getProperty("driverClass"));
            url = properties.getProperty("url");
            username = properties.getProperty("username");
            password = properties.getProperty("password");
        } catch (IOException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
    //获取连接
    public static Connection getConn() throws SQLException, ClassNotFoundException {
        Connection conn = null;
        conn = DriverManager.getConnection(url,username,password);
        return conn;
    }
    //关闭资源
    public static void close(Connection conn, Statement st, ResultSet rs){
        if (rs!= null){
            try {
                rs.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        if (st!= null){
            try {
                st.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        if (conn!= null){
            try {
                conn.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }
}
public class test01 {
    @Test
    public void insert() throws SQLException, ClassNotFoundException {
        //1.获取连接
        Connection conn = JDBCUtils.getConn();
        //2.准备sql
        String sql = "insert into category(cname) values(?);";
        //3.准备执行平台
        PreparedStatement preparedStatement = conn.prepareStatement(sql);
        /*
        * 执行sql mysql默认情况下,会把多条要执行的sql拆开来,一个一个执行
        * 如果要是批处理,可以理解为将多条要执行的sql看成是一组操作,不拆分执行
        * */
        for (int i = 0; i < 100; i++) {
            preparedStatement.setObject(1,"箱包"+i);
            /*
            * void addBatch()
            * 将一组参数添加到此PreparedStatement对象的批处理命令中
            * */
            preparedStatement.addBatch();
        }
        /*
        * 批量执行
        * executeBatch() 将一批命令提交给数据库来执行,全部命令执行成功
        * */
        preparedStatement.executeBatch();
        //关闭资源
        JDBCUtils.close(conn,preparedStatement,null);
    }
}

连接池

1.为啥要学连接池
我们平时要频繁的创建连接,销毁链接,比较耗费内存资源,所以我们需要学习连接池,里面装有很多条连接对象,使用的时候从连接池中获取,使用完毕后,归还连接池
2.连接池接口:DateSource ------ 连接池的一个标准
3.常见的连接池
Druid c3P0
在这里插入图片描述

连接池之c3p0(拓展)

1.导入c3p的jar包
c3p0-0.9.1.2.jar
2.创建xml配置文件
a.创建file
b.取名:xxx.xml -> c3p0-config.xml ----> 文件名不能错
3.xml 文件内容:复制粘贴

 <c3p0-config>
    <!-- 使用默认的配置读取连接池对象 -->
    <default-config>
        <!--  连接参数 -->
        <property name="driverClass">com.mysql.cj.jdbc.Driver</property>
        <property name="jdbcUrl">jdbc:mysql://192.168.10.100:3306/bigdata?rewriteBatchedStatements=true&amp;useUnicode=true&amp;characterEncoding=UTF8</property>
        <property name="user">root</property>
        <property name="password">root</property>

        <!--
          连接池参数
          初始连接数(initialPoolSize):刚创建好连接池的时候准备的连接数量
          最大连接数(maxPoolSize):连接池中最多可以放多少个连接
          最大等待时间(checkoutTimeout):连接池中没有连接时最长等待时间
          最大空闲回收时间(maxIdleTime):连接池中的空闲连接多久没有使用就会回收
         -->
        <property name="initialPoolSize">5</property>
        <property name="maxPoolSize">10</property>
        <property name="checkoutTimeout">2000</property>
        <property name="maxIdleTime">1000</property>
    </default-config>
  </c3p0-config>  

注意jdbcUrl的内容在xml中,用&amp加分号 用来替代&
编写c3p0工具类
1.接口:DataSource接口
2.实现类:ComboPoolDataSource()
3.创建:
dataSource = new ComboPoolDataSource()
4.获取连接:
dataSource.getConnection()
以下是代码实现

import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;

public class C3P0Utils {
    //声明一个连接池对象
    private static DataSource dataSource = null;
    /*
    * 注册驱动,数据库url,用户名,密码
    * 这四大参数应该上来就先初始化
    * 而且只需要初始化一次即可
    * 所以我们应该放到静态代码中
    * */
    static{
        dataSource = new ComboPooledDataSource();
    }
    /*
    * 获取连接
    * */
    public static Connection getConn(){
        Connection connection = null;
        try {
            connection = dataSource.getConnection();
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return connection;
    }
    /*
    * 关闭资源
    * 此时Connection的close不是销毁对象而是归还连接池
    * */
    public static  void  close(Connection connection, Statement statement, ResultSet resultSet){
        if (connection != null){
            try {
                connection.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        if (statement != null){
            try {
                statement.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        if (resultSet != null){
            try {
                resultSet.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }
}

测试代码

public class Demo01_C3 {
    @Test
    public void insert() throws SQLException {
        //获取连接
        Connection connection = C3P0Utils.getConn();
        System.out.println(connection);
        //编写sql
        String sql = "insert into category(cname) values(?);";
        //获取preparestatement对象
        PreparedStatement preparedStatement = connection.prepareStatement(sql);
        //给?赋值
        preparedStatement.setObject(1,"蔬菜");
        //执行sql
        preparedStatement.executeUpdate();
        //关闭资源
        C3P0Utils.close(connection,preparedStatement,null);
    }
}

1.xml:可拓展性标记语言 ---- 标签名自定义 标记语言:所有的内容,都是由标签组成
2.标签
a:闭合标签:一个标签由开始标签和结束标签组成
标签体
b: 自闭合标签:

连接池之Druid(德鲁伊)

1.概述:是阿里巴巴开发的,号称目前商业界最可靠的连接池
2.导jar包:
druid-1.1.10.jar
3.准备druid的properties配置文件
a.取名:druid.properties
b.写配置:
driver=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://localhost:3306/220227_java4
username=root
password=root
initialSize=5
maxActive=10
maxWait=1000
4.怎么读取配置文件
DruidDataSourceFactory.createDataSource(properties集合)
代码示例

driver=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://192.168.10.100:3306/bigdata?useUnicode=true&characterEncoding=UTF8
username=root
password=12345678
initialSize=5
maxActive=10
maxWait=1000

Druid工具类

public class DruidUtils {
    //声明一个连接池对象
    private static DataSource dataSource = null;
    /*
    * 注册驱动,数据库url,用户名 密码
    * 这四大参数应该上来就先初始化
    * 而且只需要初始化一次即可
    * 所以我们应该放到静态代码块中
    * */
    static {
        Properties properties = new Properties();
        InputStream in = DruidUtils.class.getClassLoader().getResourceAsStream("druid.properties");
        try {
            properties.load(in);
            dataSource = DruidDataSourceFactory.createDataSource(properties);
        } catch (IOException e) {
            e.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    //获取连接
    public static Connection getConn(){
        Connection conn = null;
        try {
            conn = dataSource.getConnection();
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return conn;
    }
    /*
    * 关闭资源
    * 此时connection的close不是销毁对象,而是归还连接池
    * */
    public static void close(Connection conn, Statement statement, ResultSet resultSet){
        if (conn != null){
            try {
                conn.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        if (statement != null){
            try {
                statement.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        if (resultSet != null){
            try {
                resultSet.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }
}

测试代码

public class test03_druid {
    public static void main(String[] args) throws SQLException {
        //获取连接
        Connection conn = DruidUtils.getConn();
        //编写sql
        String sql  = "insert into category(cname) values(?);";
        //获取PreparedStatement对象
        PreparedStatement preparedStatement = conn.prepareStatement(sql);
        //给?赋值
        preparedStatement.setObject(1,"猪肉");
        //执行sql
        preparedStatement.executeUpdate();
        //关闭资源
        DruidUtils.close(conn,preparedStatement,null);
    }
}

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

瑞吉外卖实战学习--11、分类管理的列表分页查询

分类管理的列表分页查询 前言1、创建接口2、基于分页组件来实现的 前言 通过前端接口可以看到请求和传递的参数&#xff0c;本文章是基于mybatisPlus的分页插件来实现的 1、创建接口 GetMapping("/page")public R<Page> page(int page,int pageSize){ // …

AI图像重绘解决方案

高质量的图像素材往往成本高昂且制作周期长&#xff0c;给企业带来了不小的困扰。美摄科技凭借其领先的AI图像重绘解决方案&#xff0c;为企业提供了一种高效、便捷且成本可控的图像优化途径&#xff0c;助力企业重塑视觉形象&#xff0c;引领市场新风尚。 美摄科技的AI图像重…

求组合数I(acwing)

题目描述&#xff1a; 给定n组询问&#xff0c;每组询问给定两个整数a&#xff0c;b&#xff0c;请你输出Ca^b mod(1e97)的值。 输入格式: 第一行包含整数n。 接下来n行&#xff0c;每行包含一组a和b。 输出格式: 共n行&#xff0c;每行输出一个询问的解。 …

FPGA设计_加法器

文章目录 前言补充&#xff1a;各种门电路符号一、半加器二、全加器三、串行进位加法器3.1、verilog代码设计 四、超前进位加法器4.1、verilog代码设计 五、进位链CARRY4 前言 在之前一篇介绍7系列FPGA底层资源的时候&#xff0c;我们提到过每一个slice当中有一个CARRY4&#…

2024.3.26学习总结

一&#xff0c;正则匹配 正则匹配是用来搜索&#xff0c;匹配&#xff0c;替换的一种字符串模式&#xff0c;使用正则匹配可以让搜索匹配的语句更加简洁&#xff0c;在php中会使用一些函数来处理正则匹配 常用的语法&#xff1a; 字符类 [abc]: 匹配单个字符a、b或c[^abc]: 匹…

为什么跟着高手还是亏损?fpmarkets10秒解答

各位投资者&#xff0c;不知道你们有没有遇见这样的情况&#xff1f;不管是别人能够持续盈利的技术指标&#xff0c;还是业内知名的行业专家&#xff0c;只要是我们这些普通的投资者一旦使用持续盈利的技术指标&#xff0c;或者跟随专家顾问的信号同时在同一个方向建仓&#xf…

Go-Gin-Example 第八部分 优化配置接口+图片上传功能

文章目录 前情提要本节目标 优化配置结构讲解落实修改配置文件优化配置读取及设置初始化顺序第一步 验证 抽离file 实现上传图片接口图片名加密封装image的处理逻辑编写上传图片的业务逻辑增加图片上传的路由 验证实现前端访问 http.FileServerr.StaticFS修改文章接口新增、更新…

基于单片机智能数字温度采集报警器系统设计

**单片机设计介绍&#xff0c;基于单片机智能数字温度采集报警器系统设计 文章目录 一 概要二、功能设计设计思路 三、 软件设计原理图 五、 程序六、 文章目录 一 概要 基于单片机智能数字温度采集报警器系统设计的核心目标是通过单片机实现温度的实时采集、显示以及超温报警…

琴童投稿发表论文

《琴童》是由国家新闻出版总署批准&#xff0c;中文天地出版传媒集团股份有限公司主管、百花洲文艺出版社有限责任公司主办的一本音乐素质教育期刊。本刊的办刊宗旨为&#xff1a;为中小学生普及音乐知识、提高音乐教育水平、促进素质教育服务。2008年、2010年、2014年、2015年…

镭速如何解决UDP传输不通的问题

我们之前有谈到过企业如果遇到UDP传输不通的情况&#xff0c;常见的一些解决方式&#xff0c;同时也介绍了一站式企业文件传输方式-镭速相关优势&#xff0c;如果在实际应用中&#xff0c;若镭速UDP传输出现不通的情况&#xff0c;需要按照网络通信的一般性排查方法以及针对镭速…

ESP32学习---ESP-NOW

ESP32学习---ESP-NOW 基于Arduino IDE环境获取mac地址单播通讯一对多通讯多对一通讯多对多通讯模块1代码模块2模块3 广播通讯 基于ESP-IDF框架 乐鑫编程指南中关于ESP-NOW的介绍&#xff1a;https://docs.espressif.com/projects/esp-idf/zh_CN/v5.2.1/esp32/api-reference/net…

探秘开发公司内部,开发小程序只要几百块?

做一个微信小程序大概需要多少钱&#xff1f; 在考虑开发微信小程序之前&#xff0c;许多商家和企业都会关心开发费用这个问题&#xff0c;并且可能会对比多家公司的报价。那么&#xff0c;开发一个微信小程序大概需要多少费用呢&#xff1f;下面我们简单介绍一下小程序开发的…

思考:开启MMU瞬间可能出现的多种问题以及多种解决方案

快速链接: 【精选】ARMv8/ARMv9架构入门到精通-[目录] &#x1f448;&#x1f448;&#x1f448; (说明本文的介绍都是基于armv8-aarch64或armv9硬件架构) 在mmu未开启阶段&#xff0c;PC操作的都是物理地址执行程序&#xff0c;这样看起来一切正常&#xff0c;没啥问题。 例如…

【Leetcode】top 100 图论

基础知识补充 1.图分为有向图和无向图&#xff0c;有权图和无权图&#xff1b; 2.图的表示方法&#xff1a;邻接矩阵适合表示稠密图&#xff0c;邻接表适合表示稀疏图&#xff1b; 邻接矩阵&#xff1a; 邻接表&#xff1a; 基础操作补充 1.邻接矩阵&#xff1a; class GraphAd…

蓝桥杯第1593题——二进制问题

题目描述 小蓝最近在学习二进制。他想知道 1 到 N 中有多少个数满足其二进制表示中恰好有 K 个 1。你能帮助他吗&#xff1f; 输入描述 输入一行包含两个整数 N 和 K。 输出描述 输出一个整数表示答案。 输入输出样例 示例 输入 7 2输出 3评测用例规模与约定 对于 30% …

软件测试工作中需要的Linux知识,一篇文章就够了

01、Linux基础 1、Linux系统简单介绍 Linux是一套免费使用, 支持多用户、多任务、支持多线程和多个核心CPU的操作系统&#xff1b;很多中型, 大型甚至是巨型项目都在使用Linux。 Linux的发行版说简单点就是将Linux与应用软件做一个打包, 目前市面上比较知名的发行版有: Ubun…

Free RTOS day2

1.思维导图 2.使用PWMADC光敏电阻完成光控灯的实验 int adc_val0;//用于保存ADC采样得到的数值 float volt0;//用于保存电压值 int main(void) {MX_GPIO_Init();MX_DMA_Init();MX_TIM1_Init();MX_USART1_UART_Init();MX_ADC_Init();MX_TIM3_Init();HAL_TIM_PWM_Start(&hti…

代码随想录算法训练营第二十七天|131.分割回文串、93.复原IP地址

文档链接&#xff1a;https://programmercarl.com/ LeetCode131.分割回文串 题目链接&#xff1a;https://leetcode.cn/problems/palindrome-partitioning/ 思路&#xff1a;把回溯的树画出来就好很多。startIndex用来控制切割的位置 例如对于字符串abcdef&#xff1a; 组…

实现offsetof宏以及交换一个整数二进制奇偶位的宏

目录 1. offsetof宏2. 交换奇偶位 1. offsetof宏 我们想用宏来实现offsetof函数,首先要了解这个函数的用法。 1.1 offsetof函数的介绍及用法 &#xff08;1&#xff09;功能&#xff1a;用来计算结构体中一个成员在该结构体中的相对起始位置的偏移量&#xff0c;单位是字节。 …

Golang goroutine 同步原语:sync 包让你对并发控制得心应手

在 Go 语言中&#xff0c;不仅有 channel 这类比较易用且高级的同步机制&#xff0c;还有 sync.Mutex、sync.WaitGroup 等比较原始的同步机制。通过它们&#xff0c;我们可以更加灵活地控制数据的同步和多协程的并发。 资源竞争 在一个 goroutine 中&#xff0c;如果分配的内存…