Java并发01:为什么需要并发编程 - 从单线程到多线程的演进

引言:一个Web服务器的故事 假设你正在开发一个简单的Web服务器,用户访问网站时,服务器需要处理请求并返回响应。最初,你可能会写出这样的代码: public class SimpleWebServer { public static void main(String[] args) throws IOException { ServerSocket serverSocket = new ServerSocket(8080); System.out.println("服务器启动,监听端口 8080..."); while (true) { // 接受客户端连接 Socket client = serverSocket.accept(); // 处理请求(耗时操作) handleRequest(client); // 关闭连接 client.close(); } } private static void handleRequest(Socket client) throws IOException { // 读取请求 BufferedReader in = new BufferedReader( new InputStreamReader(client.getInputStream())); String request = in.readLine(); // 模拟业务处理:查询数据库、调用外部API等(耗时500ms) try { Thread.sleep(500); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } // 返回响应 PrintWriter out = new PrintWriter(client.getOutputStream(), true); out.println("HTTP/1.1 200 OK"); out.println("Content-Type: text/plain"); out.println(); out.println("Hello, World!"); } } 问题来了: ...

2025-11-20 · maneng

为什么需要计算机网络

引言 想象一下,如果你是一家公司的老板,有100个员工分布在不同楼层。你需要通知所有人开会,你会怎么做? 方案1:跑到每个人面前口头通知 - 效率极低 方案2:打印100份通知,挨个发放 - 浪费资源 方案3:发一封群发邮件 - 秒级送达✅ 这就是网络的价值:高效传递信息。 计算机网络的本质就是:让不同的计算机之间能够相互通信,共享资源和数据。 第一性原理:信息传递的需求 什么是第一性原理? 第一性原理(First Principles)是一种思维方式:从最基本的事实出发,逐步推导出结论,而不是基于经验和类比。 计算机网络的第一性原理 人类天然需要沟通和协作 原始社会:语言交流 农业社会:书信往来 工业社会:电话电报 信息社会:互联网 计算机也需要交换数据 单台计算机能力有限 数据需要共享(文件、数据库) 计算资源需要共享(打印机、服务器) 协同工作需要通信(邮件、聊天、视频会议) 网络是解决方案 通过物理介质(网线、光纤、无线)连接计算机 通过协议(规则)规范通信方式 通过路由(寻址)找到目标计算机 从单机到网络的演进 1. 单机时代(1950s-1960s) 特点: 一台计算机只能一个人使用 数据存储在本地 无法与其他计算机交换数据 问题: ❌ 资源浪费(一台计算机成本高昂) ❌ 数据孤岛(无法共享数据) ❌ 协作困难(靠人工传递磁带/打孔卡片) 2. 局域网时代(1970s-1980s) 特点: 同一办公室/建筑内的计算机连接 共享打印机、文件服务器 以太网(Ethernet)技术诞生 价值: ✅ 资源共享(一台打印机服务多人) ✅ 数据共享(中心化文件服务器) ✅ 协作提升(局域网邮件、文件传输) 局限: ⚠️ 仅限于小范围 ⚠️ 无法跨地域通信 3. 广域网时代(1980s-1990s) 特点: 跨城市、跨国家的网络 TCP/IP协议成为标准 互联网(Internet)诞生 价值: ...

2025-11-20 · maneng

风险与边界:跨境出口的终局思考 | 跨境出口第一性原理(六·终)

系列说明 本文是《跨境出口第一性原理》系列第6篇(终篇),共6篇。 前5篇分别讨论了"复杂度来源"、“最小可行模型”、“平台生态”、“流量系统”、“供应链智能化”。 本篇聚焦风险与边界——跨境出口的长期可持续性。 引子:三个卖家的不同命运 卖家A:一夜回到解放前 老张的悲剧(2023年6月): 背景: ├─ 跨境电商老卖家(5年经验) ├─ 年销售额:$300万 ├─ 主营:蓝牙耳机、充电宝 ├─ 平台:Amazon美国站 └─ 月利润:$50000 事件时间线: 6月1日: ├─ 收到Amazon警告邮件 ├─ 标题:"Account Health Issue" ├─ 内容:涉嫌刷单(Brushing) └─ 老张慌了:"我没刷单啊!" 6月3日: ├─ 账号被暂停(Suspended) ├─ 所有Listing下架 ├─ FBA库存:5000个商品($150000价值) ├─ 账户余额:$80000 └─ 全部冻结! 6月5日-7月5日(申诉期): ├─ 提交申诉(Plan of Action) ├─ 解释:没有刷单,是竞争对手恶意攻击 ├─ 提供证据:订单记录、物流凭证 ├─ 等待...等待...等待... └─ 结果:申诉被拒绝(Appeal Denied) 7月10日: ├─ 账号永久封禁(Permanently Deactivated) ├─ FBA库存:可以移除(但需支付$0.50/个移除费) │ └─ 5000个 × $0.50 = $2500 ├─ 账户余额$80000: │ ├─ Amazon审查90天 │ ├─ 扣除潜在退款、索赔 │ └─ 预计能拿回$50000(损失$30000) └─ 总损失:$150000(库存)+$30000(余额)=$180000 连锁反应: ├─ 供应商货款:$50000(未付清) ├─ 货代费用:$20000(未付) ├─ 家庭开支:每月$10000 ├─ 无收入来源 └─ 生意崩盘! 反思: ❌ 100%依赖单一平台(Amazon) ❌ 没有备用账号 ❌ 没有多平台布局 ❌ 对平台规则理解不足 └─ 结论:平台风险是生死风险! 卖家B:侵权赔了50万 小李的教训(2023年9月): ...

2025-11-03 · maneng

供应链智能化:选品、备货、成本优化 | 跨境出口第一性原理(五)

系列说明 本文是《跨境出口第一性原理》系列第5篇,共6篇。 前4篇分别讨论了"复杂度来源"、“最小可行模型”、“平台生态”、“流量系统”。 本篇聚焦供应链智能化——跨境出口的核心竞争力。 引子:两个卖家的不同结局 场景A:爆款变滞销,亏损50万 小张的悲剧: 时间线(2024年3月-8月): 3月:选品决策 ├─ 看到竞品月销5000单 ├─ 判断:"这个品类很火,我也要做!" ├─ 没有深入分析数据 └─ 决定:跟风做同款 4月:大量备货 ├─ 首批订单:5000个(担心断货) ├─ 单价:$4/个,总成本:$20000 ├─ 海运头程:$5000 ├─ FBA入仓:5000个 └─ 总投入:$25000 5月:上架销售 ├─ 定价:$29.99(对标竞品) ├─ 广告预算:$3000/月 ├─ 第一个月销量:200单 ├─ 库存:4800个(库存压力开始) └─ 发现:竞争比想象中激烈 6月:销量下滑 ├─ 月销:150单(下降25%) ├─ 原因分析: │ ├─ 新增竞品:20个(竞争白热化) │ ├─ 价格战:竞品降至$24.99 │ ├─ Review差距:竞品500+,自己只有15个 │ └─ 自然排名:从第2页跌至第5页 ├─ 库存:4650个(6个月库存!) └─ 焦虑:要不要降价? 7月:被迫降价 ├─ 降价至$24.99(跟随竞品) ├─ 同时增加广告:$5000/月(烧钱抢流量) ├─ 月销:300单(销量上升) ├─ 但: │ ├─ 毛利润:$24.99 - $15 = $9.99/单 │ ├─ 广告成本:$5000 / 300 = $16.67/单 │ └─ 净利润:-$6.68/单(亏损!) └─ 库存:4350个(还有14个月库存!) 8月:滞销危机 ├─ 发现:库存周转率极低 ├─ FBA长期仓储费: │ ├─ 超过180天:$6.90/立方英尺/月 │ ├─ 4000个商品≈80立方英尺 │ └─ 月仓储费:$552(持续亏损) ├─ 决策: │ ├─ 选项1:继续降价清仓($19.99)→亏更多 │ ├─ 选项2:移除库存回国→$0.50/个移除费 │ └─ 选项3:FBA销毁→$0.15/个销毁费 └─ 最终决定:以$19.99清仓,半年亏损$50000 失败复盘: ❌ 选品盲目(只看表面热度,不看数据) ❌ 备货过量(首批5000个,太激进) ❌ 没有测款(应该先100-300个测试市场) ❌ 竞争分析不足(没预判竞品涌入) ❌ 成本核算错误(没算仓储费、长期库存成本) ❌ 没有退出机制(应该早点止损) 场景B:数据驱动,6个月回本 小王的成功: ...

2025-11-03 · maneng

流量系统:从0到1的增长引擎 | 跨境出口第一性原理(四)

系列说明 本文是《跨境出口第一性原理》系列第4篇,共6篇。 前3篇分别讨论了"复杂度来源"、“最小可行模型”、“平台生态”。 本篇聚焦流量系统——跨境出口最核心的增长引擎。 引子:两个卖家的命运 场景A:产品优秀,但无人问津 小李的困境: 时间线(2024年6月): D+0 上架充电宝(Listing优化完美) ├─ 主图:5张高清图,专业拍摄 ├─ 标题:关键词布局合理 ├─ 价格:$29.99(市场中位数) ├─ FBA配送(Prime标识) └─ 产品质量:优秀(真材实料) D+7 销量:0单 └─ 自然排名:第237页(无人能见) D+14 销量:1单(朋友帮忙买的) └─ 自然排名:第189页 D+30 销量:3单 ├─ 广告花费:$200 ├─ 广告点击:1000次(CPC $0.20) ├─ 广告转化:3单(转化率0.3%) └─ ROI:-90%(亏损严重) D+60 决定:放弃 ├─ 累计销量:5单 ├─ 累计亏损:$500(广告)+ $5000(库存) └─ 原因:"产品很好,但没人看到" 失败原因分析: 问题诊断: ❌ 没有流量来源 └─ 新品无历史销量 → 自然排名极低 → 曝光量=0 ❌ 广告策略错误 └─ 盲目投放 → 转化率极低 → 烧钱无效果 ❌ 冷启动失败 └─ 没有初始订单 → 算法不推荐 → 恶性循环 ❌ 没有站外引流 └─ 100%依赖Amazon自然流量 场景B:产品普通,但月销5000单 小王的成功: ...

2025-11-03 · maneng

最小可行模型:一个充电宝出口美国的完整推演

引子:如果没有监管会怎样? 深圳,华强北。小李有1000个充电宝,想卖给美国的消费者。 如果这个世界没有海关、没有监管、没有限制,这笔交易会是什么样的? 让我们从最简单的模型开始,逐步加入现实世界的复杂度,看看每一层复杂度背后的必然性。 场景0:理想世界 - 最简单的跨境交易 假设条件 假设1:没有海关(中国和美国之间没有边界管制) 假设2:没有监管(不需要报关、不需要缴税) 假设3:物流畅通(有国际快递公司,价格合理) 假设4:支付便捷(可以直接收美元) 假设5:完全信任(买家相信卖家不会骗人) 交易流程 步骤1:小李在论坛发帖 ├─ 标题:出售10000mAh充电宝,$25/个 ├─ 照片:产品实拍图 └─ 联系方式:邮箱/WhatsApp 步骤2:美国买家Tom看到帖子 ├─ 感兴趣,联系小李 ├─ 询问:能发到美国吗?多少运费? └─ 小李回复:可以,运费$8 步骤3:达成协议 ├─ Tom:我要5个充电宝 ├─ 小李:好的,总价$125($25×5)+ 运费$8 = $133 └─ Tom:OK,PayPal付款给你 步骤4:小李收款 ├─ Tom通过PayPal转账$133 ├─ 小李收到款(扣除手续费3%,实收$129) └─ 小李确认收款 步骤5:小李发货 ├─ 打包5个充电宝 ├─ 用DHL国际快递寄出 ├─ 填写Tom的地址 └─ 运费$40(DHL实际价格) 步骤6:Tom收货 ├─ 7天后,Tom收到包裹 ├─ 开箱验货,满意 └─ 交易完成 成本与利润分析 小李的成本: ├─ 产品成本:$4/个 × 5 = $20 ├─ 运费成本:$40(DHL) ├─ PayPal手续费:$133 × 3% = $4 └─ 总成本:$64 小李的收入: └─ $133(Tom支付) 小李的利润: └─ $133 - $64 = $69 └─ 利润率:52% Tom的总支出: └─ $133(商品$125 + 运费$8) 这个模型的问题 看起来很完美?实则问题重重: ...

2025-11-03 · maneng

Java并发第一性原理:为什么并发如此困难?

引子:一个秒杀系统的困局 假设你正在开发一个电商秒杀系统,库存100件商品,瞬间涌入1000个并发请求。看似简单的需求,却隐藏着并发编程最本质的困难。 场景A:单线程实现(简单但性能差) /** * 单线程秒杀服务 * 优势:简单、可预测、无并发问题 * 劣势:性能差,无法处理高并发 */ public class SingleThreadSeckillService { private int stock = 100; // 库存 public synchronized boolean seckill(String userId) { // 检查库存 if (stock <= 0) { System.out.println("库存不足,秒杀失败"); return false; } // 模拟业务处理(数据库操作、支付调用等) try { Thread.sleep(10); // 10ms的业务处理时间 } catch (InterruptedException e) { Thread.currentThread().interrupt(); } // 扣减库存 stock--; System.out.println("用户 " + userId + " 秒杀成功,剩余库存:" + stock); return true; } } // 性能测试 public class SingleThreadTest { public static void main(String[] args) { SingleThreadSeckillService service = new SingleThreadSeckillService(); long start = System.currentTimeMillis(); // 1000个用户顺序秒杀 for (int i = 0; i < 1000; i++) { service.seckill("User-" + i); } long end = System.currentTimeMillis(); System.out.println("总耗时:" + (end - start) + "ms"); } } /* 执行结果: 总耗时:10,000ms(10秒) 分析:每个请求10ms,1000个请求串行执行,总计10秒 问题:在真实秒杀场景中,10秒是不可接受的响应时间 */ 性能瓶颈分析: ...

2025-11-03 · maneng

MySQL第一性原理:为什么我们需要数据库?

引言 “如果你不能简单地解释一件事,说明你还没有真正理解它。” —— 理查德·费曼 作为一名后端开发者,你一定写过这样的代码: @Autowired private UserRepository userRepository; public User findUser(Long id) { return userRepository.findById(id); } 这行代码背后,数据库帮你做了什么? 持久化存储:数据写入磁盘,重启不丢失 快速检索:1亿条数据中找到目标行,只需10微秒 并发控制:1万个并发请求,数据不会错乱 事务保证:转账操作要么全成功,要么全失败 故障恢复:系统崩溃后,数据可以完整恢复 但你有没有想过: 为什么不用文件存储数据?(txt、csv、json) 为什么不用内存存储数据?(HashMap、Redis) 为什么一定要用MySQL这样的关系型数据库? 今天,我们从第一性原理出发,通过对比文件、内存、MySQL三种存储方案,深度剖析数据库解决的核心问题。 本文不会教你怎么写SQL,而是回答为什么需要数据库。 一、引子:用户注册功能的三种实现 让我们从一个最简单的需求开始:用户注册功能。 需求: 用户注册时,存储用户名和密码 检查用户名是否已存在 用户登录时,验证用户名和密码 按注册时间范围查询用户 性能要求: 支持100万用户 注册和登录响应时间 < 100ms 支持1000并发 我们将用三种方式实现,看看它们的差异。 1.1 场景A:文件存储(users.txt) 实现思路:将用户数据存储在文本文件中,每行一个用户,逗号分隔字段。 /** * 用户服务 - 文件存储实现 */ public class FileUserService { private static final String FILE_PATH = "users.txt"; /** * 注册用户(追加到文件末尾) */ public void register(String username, String password) { // 1. 检查用户名是否已存在 if (exists(username)) { throw new RuntimeException("用户名已存在"); } // 2. 追加到文件 try (FileWriter fw = new FileWriter(FILE_PATH, true)) { String line = username + "," + hashPassword(password) + "," + System.currentTimeMillis() + "\n"; fw.write(line); } catch (IOException e) { throw new RuntimeException("注册失败", e); } } /** * 检查用户名是否存在(全文件扫描) */ public boolean exists(String username) { try (BufferedReader br = new BufferedReader(new FileReader(FILE_PATH))) { String line; while ((line = br.readLine()) != null) { // O(n) 全表扫描 String[] parts = line.split(","); if (parts[0].equals(username)) { return true; } } } catch (IOException e) { e.printStackTrace(); } return false; } /** * 登录验证(全文件扫描) */ public boolean login(String username, String password) { String hashedPassword = hashPassword(password); try (BufferedReader br = new BufferedReader(new FileReader(FILE_PATH))) { String line; while ((line = br.readLine()) != null) { // O(n) 全表扫描 String[] parts = line.split(","); if (parts[0].equals(username) && parts[1].equals(hashedPassword)) { return true; } } } catch (IOException e) { e.printStackTrace(); } return false; } /** * 按注册时间范围查询(全文件扫描+过滤) */ public List<User> findByRegisteredDateRange(long startTime, long endTime) { List<User> result = new ArrayList<>(); try (BufferedReader br = new BufferedReader(new FileReader(FILE_PATH))) { String line; while ((line = br.readLine()) != null) { // O(n) 全表扫描 String[] parts = line.split(","); long registeredTime = Long.parseLong(parts[2]); if (registeredTime >= startTime && registeredTime <= endTime) { result.add(new User(parts[0], parts[1], registeredTime)); } } } catch (IOException e) { e.printStackTrace(); } return result; } private String hashPassword(String password) { // 简化版,实际应该用BCrypt return Integer.toString(password.hashCode()); } } 文件内容示例: ...

2025-11-03 · maneng

MySQL第一性原理:为什么我们需要数据库?

引言 “如果你不能简单地解释一件事,说明你还没有真正理解它。” —— 理查德·费曼 作为一名后端开发者,你一定写过这样的代码: @Autowired private UserRepository userRepository; public User findUser(Long id) { return userRepository.findById(id); } 这行代码背后,数据库帮你做了什么? 持久化存储:数据写入磁盘,重启不丢失 快速检索:1亿条数据中找到目标行,只需10微秒 并发控制:1万个并发请求,数据不会错乱 事务保证:转账操作要么全成功,要么全失败 故障恢复:系统崩溃后,数据可以完整恢复 但你有没有想过: 为什么不用文件存储数据?(txt、csv、json) 为什么不用内存存储数据?(HashMap、Redis) 为什么一定要用MySQL这样的关系型数据库? 今天,我们从第一性原理出发,通过对比文件、内存、MySQL三种存储方案,深度剖析数据库解决的核心问题。 本文不会教你怎么写SQL,而是回答为什么需要数据库。 一、引子:用户注册功能的三种实现 让我们从一个最简单的需求开始:用户注册功能。 需求: 用户注册时,存储用户名和密码 检查用户名是否已存在 用户登录时,验证用户名和密码 按注册时间范围查询用户 性能要求: 支持100万用户 注册和登录响应时间 < 100ms 支持1000并发 我们将用三种方式实现,看看它们的差异。 1.1 场景A:文件存储(users.txt) 实现思路:将用户数据存储在文本文件中,每行一个用户,逗号分隔字段。 /** * 用户服务 - 文件存储实现 */ public class FileUserService { private static final String FILE_PATH = "users.txt"; /** * 注册用户(追加到文件末尾) */ public void register(String username, String password) { // 1. 检查用户名是否已存在 if (exists(username)) { throw new RuntimeException("用户名已存在"); } // 2. 追加到文件 try (FileWriter fw = new FileWriter(FILE_PATH, true)) { String line = username + "," + hashPassword(password) + "," + System.currentTimeMillis() + "\n"; fw.write(line); } catch (IOException e) { throw new RuntimeException("注册失败", e); } } /** * 检查用户名是否存在(全文件扫描) */ public boolean exists(String username) { try (BufferedReader br = new BufferedReader(new FileReader(FILE_PATH))) { String line; while ((line = br.readLine()) != null) { // O(n) 全表扫描 String[] parts = line.split(","); if (parts[0].equals(username)) { return true; } } } catch (IOException e) { e.printStackTrace(); } return false; } /** * 登录验证(全文件扫描) */ public boolean login(String username, String password) { String hashedPassword = hashPassword(password); try (BufferedReader br = new BufferedReader(new FileReader(FILE_PATH))) { String line; while ((line = br.readLine()) != null) { // O(n) 全表扫描 String[] parts = line.split(","); if (parts[0].equals(username) && parts[1].equals(hashedPassword)) { return true; } } } catch (IOException e) { e.printStackTrace(); } return false; } /** * 按注册时间范围查询(全文件扫描+过滤) */ public List<User> findByRegisteredDateRange(long startTime, long endTime) { List<User> result = new ArrayList<>(); try (BufferedReader br = new BufferedReader(new FileReader(FILE_PATH))) { String line; while ((line = br.readLine()) != null) { // O(n) 全表扫描 String[] parts = line.split(","); long registeredTime = Long.parseLong(parts[2]); if (registeredTime >= startTime && registeredTime <= endTime) { result.add(new User(parts[0], parts[1], registeredTime)); } } } catch (IOException e) { e.printStackTrace(); } return result; } private String hashPassword(String password) { // 简化版,实际应该用BCrypt return Integer.toString(password.hashCode()); } } 文件内容示例: ...

2025-11-03 · maneng

Redis第一性原理:为什么我们需要缓存?

一、引子:商品详情接口的性能进化之路 想象你正在开发一个电商平台的商品详情页面,每次用户访问都需要查询商品基本信息、品牌信息、类目信息、库存信息和商品图片。让我们看看三种不同的实现方式,以及它们各自的性能表现。 1.1 场景A:无缓存(直接查数据库) 这是最直接的实现方式:每次请求都查询数据库。 @Service public class ProductService { @Autowired private ProductRepository productRepository; @Autowired private BrandRepository brandRepository; @Autowired private CategoryRepository categoryRepository; @Autowired private InventoryRepository inventoryRepository; @Autowired private ProductImageRepository productImageRepository; /** * 查询商品详情(每次请求都查数据库) * 平均耗时:100ms * QPS上限:500 */ public ProductDetailVO getProductDetail(Long productId) { // 1. 查询商品基本信息(20ms) Product product = productRepository.findById(productId); if (product == null) { throw new ProductNotFoundException("商品不存在:" + productId); } // 2. 查询品牌信息(20ms) Brand brand = brandRepository.findById(product.getBrandId()); // 3. 查询类目信息(20ms) Category category = categoryRepository.findById(product.getCategoryId()); // 4. 查询库存信息(20ms) Inventory inventory = inventoryRepository.findByProductId(productId); // 5. 查询商品图片(20ms,可能有N+1查询问题) List<ProductImage> images = productImageRepository.findByProductId(productId); // 6. 组装返回对象 ProductDetailVO vo = new ProductDetailVO(); vo.setProductId(product.getId()); vo.setProductName(product.getName()); vo.setPrice(product.getPrice()); vo.setBrandName(brand.getName()); vo.setCategoryName(category.getName()); vo.setStock(inventory.getStock()); vo.setImages(images); return vo; } } 性能数据(压测工具:JMeter,1000并发): ...

2025-11-03 · maneng

如约数科科技工作室

浙ICP备2025203501号

👀 本站总访问量 ...| 👤 访客数 ...| 📅 今日访问 ...