当前位置: 首页 > news >正文

Java入门级教程13-多线程同步安全机制synchronized(内置锁)、JavaMail发送电子邮箱、爬取CSDN到邮箱、备份数据库

目录

1.Java多线程同步安全机制——synchronized(内置锁)

1.1 synchronized 对同一个资源加锁才能锁得住

1.1.1 对不同资源

1.1.2 对同一个资源

1.2 synchronized关键字用于单例模式创建对象

1.2.1 未使用synchronized关键字

1.2.2 使用synchronized关键字

1.3 余额变化

1.3.1 未使用synchronized关键字

1.3.2 使用synchronized关键字

1.3.3 使用synchronized同步块

2.使用JavaMail发送电子邮箱📮

2.1 加载Maven依赖

2.2 QQ邮箱授权

2.2.1 老版QQ邮箱授权

2.2.2 新版QQ邮箱授权

2.3 具体实现步骤

2.3.1 创建MailRunnable类

2.3.2 创建测试类

2.3.3 验证邮件是否发送成功

3.爬取CSDN主页博客信息,并发送到邮箱

3.1 爬取CSDN主页博客信息

3.2 邮件发送任务

3.3 创建定时器,定时发送邮件

4.每天备份数据库文件


1.Java多线程同步安全机制——synchronized(内置锁)

通过加锁机制保证同一时刻只有一个线程能执行特定代码,从而避免多个线程同时操作共享资源导致的数据不一致

synchronized又叫同步锁,暗锁和自动释放锁

1.1 synchronized 对同一个资源加锁才能锁得住

1.1.1 对不同资源

当多线程操作不同对象实例的synchronized方法时,因锁对象不同,线程间无竞争,同步失效(可并行执行)

package com.hy.chapter8;public class UserThread extends Thread {public synchronized void run() {for (int i = 0; i <= 5; i++) {System.out.println(Thread.currentThread().getName() + ",执行" + i);}}public static void main(String[] args) {UserThread u1 = new UserThread(); // 实例1:锁对象是u1UserThread u2 = new UserThread(); // 实例2:锁对象是u2u1.start(); // 线程1持有的锁是u1u2.start(); // 线程2持有的锁是u2}}

输出结果:可能乱序

Thread-1,执行0
Thread-1,执行1
Thread-1,执行2
Thread-1,执行3
Thread-0,执行0
Thread-0,执行1
Thread-0,执行2
Thread-0,执行3
Thread-0,执行4
Thread-0,执行5
Thread-1,执行4
Thread-1,执行5

1.1.2 对同一个资源

当多线程操作同一个对象实例的synchronized方法时,锁机制生效,线程会排队执行(互斥)

package com.hy.chapter8;public class UserThread extends Thread {public synchronized void run() {for (int i = 0; i <= 5; i++) {System.out.println(Thread.currentThread().getName() + ",执行" + i);}}public static void main(String[] args) {UserThread u1 = new UserThread(); // 唯一实例:锁对象是u1Thread t1 = new Thread(u1); // 线程1基于u1创建,执行u1的run方法Thread t2 = new Thread(u1); // 线程2基于u1创建,执行u1的run方法t1.start();t2.start();}}

输出结果:顺序固定

Thread-1,执行0
Thread-1,执行1
Thread-1,执行2
Thread-1,执行3
Thread-1,执行4
Thread-1,执行5
Thread-2,执行0
Thread-2,执行1
Thread-2,执行2
Thread-2,执行3
Thread-2,执行4
Thread-2,执行5

1.2 synchronized关键字用于单例模式创建对象

多线程环境下的懒汉式单例模式实现,核心是通过synchronized关键字保证单例对象的唯一性

1.2.1 未使用synchronized关键字

package com.hy.chapter9;class User {private static User u;private User() {}public static User getInstance() {if (null == u) {System.out.println(Thread.currentThread().getName() + "创建对象");u = new User();}return u;}
}class UserThread1 extends Thread {public void run() {User u1 = User.getInstance();System.out.println(u1);}
}class UserThread2 extends Thread {public void run() {User u2 = User.getInstance();System.out.println(u2);}
}public class Test {public static void main(String[] args) {UserThread1 u1 = new UserThread1();UserThread2 u2 = new UserThread2();u1.start();u2.start();}}

输出结果:

Thread-0创建对象
Thread-1创建对象
com.hy.chapter9.User@370720d1
com.hy.chapter9.User@552a72f3

1.2.2 使用synchronized关键字

package com.hy.chapter9;class User {private static User u;private User() {}// 懒汉式单例,用到对象才创建public synchronized static User getInstance() {if (u == null) {System.out.println(Thread.currentThread().getName() + "创建对象");u = new User();}return u;}
}class UserThread1 extends Thread {public void run() {User u1 = User.getInstance();System.out.println(u1);}
}class UserThread2 extends Thread {public void run() {User u2 = User.getInstance();System.out.println(u2);}
}public class Test {public static void main(String[] args) {UserThread1 u1 = new UserThread1();UserThread2 u2 = new UserThread2();u1.start();u2.start();}}

输出结果:

package com.hy.chapter7;public class Test {public static void main(String[] args) {UserRunable ur = new UserRunable();Thread t1 = new Thread(ur);Thread t2 = new Thread(ur);t1.start();t2.start();}}/**
*输出结果:
*Thread-1,执行0......Thread-1,执行10
*Thread-0,执行0......Thread-0,执行10
*
*或者:
*Thread-0,执行0......Thread-0,执行10
*Thread-1,执行0......Thread-1,执行10
*
*/

Thread-0创建对象
com.hy.chapter9.User@5b74b597
com.hy.chapter9.User@5b74b597

1.3 余额变化

1.3.1 未使用synchronized关键字

代码若未加同步锁,多线程并发修改共享资源money时,会出现数据不一致(结果不可控),存在线程安全问题。

package com.hy.chapter10;// synchronized同步,就是加锁的操作,保证多线程竞争同一个资源时的安全。
// 单例模式,在多线程情况下,会创建多个对象,怎么保证单例对象,synchronized// 银行类
class Bank {// 卡号private String bankNumber = "";// 账户的金额private double money = 0.0;public Bank(String bankNumber, double money) {this.bankNumber = bankNumber;this.money = money;}// 操作银行账户的方法public void operatorBank(double operatorMoney) {this.money += operatorMoney;System.out.println(Thread.currentThread().getName() + ",操作的金额是:" + operatorMoney + ",现在账户剩余的金额是:" + this.money);}
}class JindongThread extends Thread {double opMoney;Bank bank;public JindongThread(String threadName, double opMoney, Bank bank) {super(threadName);this.opMoney = opMoney;this.bank = bank;}public void run() {this.bank.operatorBank(this.opMoney);}
}class WeixinThread extends Thread {double opMoney;Bank bank;public WeixinThread(String threadName, double opMoney, Bank bank) {super(threadName);this.opMoney = opMoney;this.bank = bank;}public void run() {this.bank.operatorBank(this.opMoney);}
}class ZhifubaoThread extends Thread {double opMoney;Bank bank;public ZhifubaoThread(String threadName, double opMoney, Bank bank) {super(threadName);this.opMoney = opMoney;this.bank = bank;}public void run() {this.bank.operatorBank(this.opMoney);}
}public class Test {public static void main(String[] args) {Bank bank = new Bank("10086", 1000.0);ZhifubaoThread z = new ZhifubaoThread("支付宝", 300, bank);WeixinThread w = new WeixinThread("微信", -400, bank);JindongThread j = new JindongThread("京东", 600, bank);z.start();w.start();j.start();}
}

输出结果:

支付宝,操作的金额是:300.0,现在账户剩余的金额是:1500.0
京东,操作的金额是:600.0,现在账户剩余的金额是:1500.0
微信,操作的金额是:-400.0,现在账户剩余的金额是:1500.0

1.3.2 使用synchronized关键字

代码通过synchronized修饰操作方法,保证了同一时间只有一个线程能修改账户金额,解决了线程安全问题,结果始终正确。

package com.hy.chapter10;//银行类
class Bank {// 卡号private String bankNumber = "";// 账户的金额private double money = 0.0;public Bank(String bankNumber, double money) {this.bankNumber = bankNumber;this.money = money;}// 操作银行账户的方法public synchronized void operatorBank(double operatorMoney) {this.money += operatorMoney;System.out.println(Thread.currentThread().getName() + ",操作的金额是:" + operatorMoney + ",现在账户剩余的金额是:" + this.money);}
}class JindongThread extends Thread {double opMoney;Bank bank;public JindongThread(String threadName, double opMoney, Bank bank) {super(threadName);this.opMoney = opMoney;this.bank = bank;}public void run() {this.bank.operatorBank(this.opMoney);}
}class WeixinThread extends Thread {double opMoney;Bank bank;public WeixinThread(String threadName, double opMoney, Bank bank) {super(threadName);this.opMoney = opMoney;this.bank = bank;}public void run() {this.bank.operatorBank(this.opMoney);}
}class ZhifubaoThread extends Thread {double opMoney;Bank bank;public ZhifubaoThread(String threadName, double opMoney, Bank bank) {super(threadName);this.opMoney = opMoney;this.bank = bank;}public void run() {this.bank.operatorBank(this.opMoney);}
}public class Test {public static void main(String[] args) {Bank bank = new Bank("10086", 1000.0);ZhifubaoThread z = new ZhifubaoThread("支付宝", 300, bank);WeixinThread w = new WeixinThread("微信", -400, bank);JindongThread j = new JindongThread("京东", 600, bank);z.start();w.start();j.start();}
}

输出结果:

支付宝,操作的金额是:300.0,现在账户剩余的金额是:1300.0
京东,操作的金额是:600.0,现在账户剩余的金额是:1900.0
微信,操作的金额是:-400.0,现在账户剩余的金额是:1500.0

1.3.3 使用synchronized同步块

在 1.2.5 的基础上优化了Bank类的实现,使用Synchronized同步块,对需要竞争的代码进行锁定,降低锁定的范围,优化性能。

package com.hy.chapter11;//银行类
class Bank {// 卡号private String bankNumber = "";// 账户的金额private double money = 0.0;public Bank(String bankNumber, double money) {this.bankNumber = bankNumber;this.money = money;}// 操作银行账户的方法 synchronized 修饰方法,会给整个方法加锁,导致整个方法被锁定,导致锁定的范围过大// synchronized 同步块,对需要竞争的代码进行锁定,降低锁定的范围,优化性能public void operatorBank(double operatorMoney) {System.out.println("欢迎您到银行办理具体的业务");synchronized (Bank.class) { // ()里面一定是引用类型对象,必须是同一个对象this.money += operatorMoney;System.out.println(Thread.currentThread().getName() + ",操作的金额是:" + operatorMoney + ",现在账户剩余的金额是:" + this.money);}System.out.println("谢谢您,欢迎下次光临");}
}// 其他类代码保持不变

输出结果:

欢迎您到银行办理具体的业务
欢迎您到银行办理具体的业务
欢迎您到银行办理具体的业务
支付宝,操作的金额是:300.0,现在账户剩余的金额是:1300.0
谢谢您,欢迎下次光临
京东,操作的金额是:600.0,现在账户剩余的金额是:1900.0
谢谢您,欢迎下次光临
微信,操作的金额是:-400.0,现在账户剩余的金额是:1500.0
谢谢您,欢迎下次光临

注意:在Synchronized( )中,( )里面一定是引用类型对象,且必须是同一个对象,例如this, bankNumber, Bank.class

1.4 票数变化

1.4.1 未使用synchronized同步块

无同步时,多线程并发操作共享变量会导致数据混乱(线程不安全)

package com.hy.chapter15;public class Buy implements Runnable {// 票数private int sum = 10;private boolean flag = true;@Overridepublic void run() {while (flag) {try {Thread.sleep(1000);System.out.println(Thread.currentThread().getName() + ",买到了票,是第" + this.sum-- + "张票");if (this.sum <= 1) {this.flag = false;}} catch (InterruptedException e) {e.printStackTrace();}}}public static void main(String[] args) {Buy b = new Buy();new Thread(b, "张三线程").start();new Thread(b, "李四线程").start();new Thread(b, "王五线程").start();}}

输出结果:

1.4.2 使用synchronized同步块

有同步时,通过互斥锁保证关键操作的原子性,解决核心线程安全问题

package com.hy.chapter15;public class Buy implements Runnable {// 票数private int sum = 10;private boolean flag = true;@Overridepublic void run() {while (flag) {try {Thread.sleep(1000);synchronized (this) {System.out.println(Thread.currentThread().getName() + ",买到了票,是第" + this.sum-- + "张票");}if (this.sum <= 1) {this.flag = false;}} catch (InterruptedException e) {e.printStackTrace();}}}public static void main(String[] args) {Buy b = new Buy();new Thread(b, "张三线程").start();new Thread(b, "李四线程").start();new Thread(b, "王五线程").start();}}

输出结果:

2.使用JavaMail发送电子邮箱📮

2.1 加载Maven依赖

<dependency><groupId>cn.hutool</groupId><artifactId>hutool-all</artifactId><version>5.8.38</version>
</dependency><dependency><groupId>com.sun.mail</groupId><artifactId>javax.mail</artifactId><version>1.6.2</version>
</dependency>

2.2 QQ邮箱授权

2.2.1 老版QQ邮箱授权

进入设置 -->账号 -->服务状态开启服务 -->点击继续获取授权码 -->微信扫码发送短信 -->发送成功后点击“我已发送”获取授权码

2.2.2 新版QQ邮箱授权

① 首页找到设置,进入设置,点击账号与安全

           

② 在安全设置中,找到POP3...服务,点击"开启服务"后,按照要求用微信扫码发送短信

③ 短信发送成功后,点击“我已发送”获取授权码

2.3 具体实现步骤

2.3.1 创建MailRunnable类

package com.hy.chapter12;import java.security.GeneralSecurityException;import com.sun.mail.util.MailSSLSocketFactory;import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.extra.mail.MailAccount;
import cn.hutool.extra.mail.MailUtil;public class MailRunnable implements Runnable {@Overridepublic void run() {System.out.println(Thread.currentThread().getName() + "准备发送邮件....");try {// 构建邮件账户配置对象MailAccount mailAccount = new MailAccount();// 配置SSL加密(关键:QQ邮箱SMTP服务要求SSL连接)mailAccount.setSslEnable(true); // 启用SSLMailSSLSocketFactory mss = new MailSSLSocketFactory("TLSv1.2"); // 设置TLS版本(兼容多数邮箱)mss.setTrustAllHosts(true); // 信任所有主机(避免证书验证失败,开发环境常用)// 配置SMTP服务器信息(以QQ邮箱为例)mailAccount.setHost("smtp.qq.com"); // QQ邮箱SMTP服务器地址mailAccount.setPort(465); // SSL连接端口(QQ邮箱常用465或587)mailAccount.setAuth(true); // 启用身份验证(必须开启)// 配置发件人信息mailAccount.setFrom("你的QQ账号@qq.com"); // 发件人邮箱(需与下面的user一致)mailAccount.setUser("你的QQ账号"); // 发件人账号(通常是邮箱前缀,如QQ号)mailAccount.setPass("你的临时授权密码"); // 发件人授权码(非QQ登录密码,需单独获取)// 发送邮件 单发// 参数:账户配置、收件人邮箱、邮件主题、邮件内容、是否HTML格式MailUtil.send(mailAccount, "QQ账号1@qq.com", "java发送邮件", "你好让我们一起学习Java", false);// 群发//MailUtil.send(mailAccount, //		CollUtil.newArrayList("QQ账号1@qq.com",//				"QQ账号2@qq.com",//				"QQ账号3@qq.com"), //		"java发送邮件机制", //		"你好让我们一起学习Java", false);// 发送带附件的邮件// 最后一个参数是附件文件(可传入多个File对象)//MailUtil.send(mailAccount,//    CollUtil.newArrayList("QQ账号1@qq.com"),//    "java发送带附件的邮件", //    "<h1>这个是html内容的邮件信息</h1>", // HTML格式内容(需将最后一个参数设为true)//    true, //    FileUtil.file("d:/cool.png") // 附件文件(Hutool的FileUtil简化文件操作)//);System.out.println("发送邮件成功....");} catch (GeneralSecurityException e) {e.printStackTrace();}}}

2.3.2 创建测试类

package com.hy.chapter12;public class Test {public static void main(String[] args) {MailRunnable m = new MailRunnable(); // 创建邮件任务Thread t = new Thread(m); // 将任务交给线程t.start(); // 启动线程(异步执行邮件发送)}
}

输出结果:

Thread-0准备发送邮件....
发送邮件成功....

2.3.3 验证邮件是否发送成功

邮件发送成功,在所写接受邮件账号的QQ邮箱中查看邮件

3.爬取CSDN主页博客信息,并发送到邮箱

3.1 爬取CSDN主页博客信息

CsdnTask:继承TimerTask,作为定时任务的具体实现 ——类加载时爬取一次 CSDN 指定博客列表页面,之后每次定时任务触发时,从爬取的结果中提取一条博客数据(标题、链接、内容等)

package com.hy.chapter13;import java.util.TimerTask;import org.jsoup.Connection;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;public class CsdnTask extends TimerTask {static int i = 1;static Elements elements;static {try {String url = "https://blog.csdn.net/weixin_74137141/article/list/1";// 1.第一步要求判断能不能去爬取这个链接Connection conn = Jsoup.connect(url);System.out.println(conn);// 2.获取这个文档对象Document doc = conn.get();// System.out.println(doc);// 3.获取整合文档docelements = doc.select(".article-list .article-item-box");} catch (Exception e) {e.printStackTrace();}}@Overridepublic void run() {try {Element e = elements.get(i);String title = e.select("a").text();String link = e.select("a").attr("href");String content = e.select(".content").text();String date = e.select(".date").text();String num = e.select(".read-num").first().text();System.out.println("一条博客的信息为:" + title + "," + link + "," + content + "," + date + "," + num);// 多线程发邮件MailRunnable m = new MailRunnable(title, link, content, date, num);new Thread(m).start();// 可以实现写入数据库。。i++;} catch (Exception e) {e.printStackTrace();}}}

3.2 邮件发送任务

MailRunnable:实现Runnable,作为邮件发送任务 —— 接收CsdnTask传递的博客数据,通过 Hutool 配置 QQ 邮箱,将数据以 HTML 格式 + 附件的形式发送到指定收件人邮箱

package com.hy.chapter13;import java.security.GeneralSecurityException;import com.sun.mail.util.MailSSLSocketFactory;import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.extra.mail.MailAccount;
import cn.hutool.extra.mail.MailUtil;public class MailRunnable implements Runnable {String title;String link;String content;String date;String num;public MailRunnable(String title, String link, String content, String date, String num) {this.title = title;this.link = link;this.content = content;this.date = date;this.num = num;}@Overridepublic void run() {// TODO Auto-generated method stubSystem.out.println(Thread.currentThread().getName() + "准备发送邮件....");try {// 构建一个邮件的MailAccount对象MailAccount mailAccount = new MailAccount();mailAccount.setSslEnable(true);MailSSLSocketFactory mss = new MailSSLSocketFactory("TLSv1.2");// 信任所有的hostmss.setTrustAllHosts(true);// 设置邮件的配置信息mailAccount.setHost("smtp.qq.com");mailAccount.setPort(465);mailAccount.setAuth(true);mailAccount.setFrom("你的QQ账号@qq.com");mailAccount.setUser("你的QQ账号");mailAccount.setPass("你的临时授权密码");MailUtil.send(mailAccount, CollUtil.newArrayList("QQ账号1@qq.com"), "csdn算法与编程之美的博客系列","<p>标题为:" + this.title + "</p>" + "<p>链接为:" + this.link + "</p>内容为:" + "<p>" + this.content + "</p>"+ "<p>发布时间为:" + this.date + "</p>",true, FileUtil.file("d:/cool.png"));System.out.println("发送邮件成功....");} catch (GeneralSecurityException e) {e.printStackTrace();}}}

3.3 创建定时器,定时发送邮件

Test类中的代码是整个 “定时爬取数据并发送邮件” 系统的入口程序,核心作用是通过Timer(Java 定时任务工具)配置并启动CsdnTask任务,实现 “延迟指定时间后开始,按随机周期重复执行爬取和邮件发送” 的自动化流程。

package com.hy.chapter13;import java.util.Random;
import java.util.Timer;//用一个线程每隔一段时间爬取一条数据,并给你的邮箱发送这条数据
public class Test {public static void main(String[] args) {// 建立一个定时器Timer t = new Timer();// 1 秒后启动任务,并以 0~80 秒的随机固定周期重复执行t.schedule(new CsdnTask(), 1 * 1000, new Random().nextInt(80000));}
}

输出结果:

邮箱查询:

详细内容:

4.每天备份数据库文件

基于 Java 实现的 MySQL 数据库备份工具,核心逻辑是通过 Java 调用系统的mysqldump.exe(MySQL 官方备份工具),将指定数据库的内容导出为 SQL 文件并保存到本地

正常查找MySQL 官方备份工具路径:(注意MySQL版本)

C: >program Files >MySQL >MySQL Server 5.7 >bin >mysqldump.exe

package com.hy.chapter14;import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.PrintWriter;public class Test1 {public static void main(String[] args) {// 1. 初始化IO流对象( BufferedReader:读备份内容;PrintWriter:写SQL文件)BufferedReader br = null;PrintWriter pw = null;// 2. 数据库备份参数(IP、用户名、密码、数据库名)String ip = "127.0.0.1";String username = "root";String userpwd = "yourpassword";String databaseName = "mysql2025";try {// 调用Runtime.exec()启动mysqldump.exe进程,执行备份命令//Process p1 = Runtime.getRuntime().exec(//		"C:\\Program Files\\MySQL\\MySQL Server 5.7\\bin\\mysqldump.exe -h127.0.0.1 -uroot -pyourpassword mysql2025");// IP和用户名和密码后面值不要由空格Process p1 = Runtime.getRuntime().exec("C:\\Program Files\\MySQL\\MySQL Server 5.7\\bin\\mysqldump.exe -h"+ ip + "  -u" + username + " -p" + userpwd + " " + databaseName);// 3.1 构建输入流:读取mysqldump进程输出的SQL内容(字节流→字符流→缓冲流,提高效率)br = new BufferedReader(new InputStreamReader(p1.getInputStream(), "UTF-8"));// 3.2 构建输出流:将SQL内容写入D盘的mysql2025.sql文件pw = new PrintWriter(new File("d:/mysql2025.sql"));// 3.3 逐行读取SQL内容,写入文件String line = "";while ((line = br.readLine()) != null) { // 读取一行内容,直到流末尾(null)pw.println(line); // 将一行SQL写入文件}pw.flush(); // 强制刷新缓冲区:确保所有内容写入文件(避免缓冲区残留数据丢失)// waitFor():阻塞当前Java线程,等待mysqldump进程执行完毕// 进程退出码为0 → 正常执行;非0 → 执行失败(如密码错误、数据库不存在)if (p1.waitFor() == 0) {System.out.println("备份数据库正常退出");}} catch (IOException e) {e.printStackTrace();} catch (Exception e) {e.printStackTrace();} finally {// 关闭BufferedReader(输入流)if (br != null) {try {br.close();} catch (IOException e) {e.printStackTrace();}}// 关闭PrintWriter(输出流)if (pw != null) {pw.close(); // PrintWriter的close()会自动刷新缓冲区,此处可省略额外flush()}}}}

注意:数据库密码、表名称等相关信息要改成自己的

输出结果:

备份数据库正常退出

输出sql文件内容:


文章转载自:

http://RthZ4o6L.hphqy.cn
http://tqzzTch4.hphqy.cn
http://HK8B3t6Z.hphqy.cn
http://6pWCtyIy.hphqy.cn
http://z8yvN7KA.hphqy.cn
http://gx9JXJPS.hphqy.cn
http://mCpHRjc9.hphqy.cn
http://yHfXBODq.hphqy.cn
http://LAq6Vrx7.hphqy.cn
http://UocXZ12X.hphqy.cn
http://2fH2H2yH.hphqy.cn
http://GftdbGYT.hphqy.cn
http://ICJVyhFb.hphqy.cn
http://JlpUqYKv.hphqy.cn
http://aG3h2e8j.hphqy.cn
http://f9MsWxPt.hphqy.cn
http://huas8PFp.hphqy.cn
http://TeT5PF6n.hphqy.cn
http://vOiSGwSP.hphqy.cn
http://pprS7kIb.hphqy.cn
http://Tvngfadh.hphqy.cn
http://K0zkQeLK.hphqy.cn
http://GZ6HNbSP.hphqy.cn
http://CzntvzZz.hphqy.cn
http://Yqlkunxt.hphqy.cn
http://qWQF05iR.hphqy.cn
http://hZf3zzWY.hphqy.cn
http://1vsj3h1c.hphqy.cn
http://QYi7xrz0.hphqy.cn
http://pR2OKuGA.hphqy.cn
http://www.dtcms.com/a/374737.html

相关文章:

  • 玩转Docker | 使用Docker部署KissLists任务管理工具
  • STL库——map/set(类函数学习)
  • STM32 串口接收数据包(自定义帧头帧尾)
  • 正向代理,反向代理,负载均衡还有nginx
  • 用户态与内核态的深度解析:安全、效率与优化之道
  • 搭建本地gitea服务器
  • ArcGIS JSAPI 高级教程 - 倾斜摄影数据开启透明(修改源码)
  • 输电线路分布式故障监测装置技术解析
  • 概率论第四讲—随机变量的数字特征
  • 学习stm32 蓝牙
  • 数据库学习MySQL系列2、Windows11系统安装MySQL方法一.msi安装详细教程
  • STM32物联网项目---ESP8266微信小程序结合OneNET平台MQTT实现STM32单片机远程智能控制---代码篇(四)
  • 北京鲁成伟业 | 三屏加固笔记本电脑C156F3
  • 从0~1搭建技术团队的思路
  • 如何在 Unity3D 中实现圆角效果?
  • LeetCode 面试经典 150 题:多数元素(摩尔投票法详解 + 多解法对比)
  • CStringArray 和 CStringList
  • 银行业安全用电系统建设与智能化管理探析
  • 20250909_排查10.1.1.190档案库房综合管理系统20250908备份缺失问题+优化scp脚本(把失败原因记录进日志)并测试脚本执行情况
  • 硬件开发_基于STM32单片机的海鲜冷藏车检测系统
  • AI一周事件(2025年9月3日-9月8日)
  • Unity3D发布的文件打包成Windows安装程序
  • 已知两个平面点的坐标、切线方向、曲率,使用牛顿迭代法构造三阶 Bézier 曲线的方法
  • 全球工业互联网大会 | 蓝卓supOS以数据底座,筑牢工业AI基石
  • k8s交互桥梁:走进Client-Go
  • K8S-Node
  • 嵌入式 - ARM(4) 硬件介绍与开发环境搭建
  • 网络上那些在线 PDF 转换工具安全吗?转换 PDF 需要注意什么
  • OneMark 插件试用
  • 专题:2025人形机器人、工业机器人、智能焊接机器人、扫地机器人产业洞察报告 | 附158+份报告PDF、数据仪表盘汇总下载