第一章 并发编程的挑战
并发编程的目的是为了让程序运行得更快,但是,并不是启动更多的线程就能让程序最大限度地并发执行,在进行并发编程时,如果希望通过多线程执行任务让程序运动得更快,会面临非常多的挑战,比如上下文切换的问题、死锁的问题,以及受限于硬件和软件的资源限制问题。
挑战一:上下文切换
多线程一定比单线程快么?
public class ConcurrencyTest { private static final long count = 10001; public static void main(String[] args) throws InterruptedException { concurrency(); serial(); } private static void concurrency() throws InterruptedException{ Long start = System.currentTimeMillis(); Thread thread = new Thread(new Runnable() { @Override public void run() { int a = 0; for (long i = 0; i < count; i++) { a += 5; } } }); thread.start(); int b = 0; for (long i = 0; i < count; i++) { b--; } thread.join(); long time = System.currentTimeMillis() - start; System.out.println("concurrency:" + time + "ms, b = " + b); } private static void serial(){ long start = System.currentTimeMillis(); int a = 0; for (long i = 0; i < count; i++) { a += 5; } int b = 0; for (long i = 0; i < count; i++) { b--; } long time = System.currentTimeMillis() - start; System.out.println("serial:" + time + "ms, b = " + b + ", a=" + a); } }
输出
答案是并不一定,当测试量达到一百万的时候,并发才能比串行优势点(本代码环境结果);
线程创建和上下文切换都是需要开销的。
如何减少上下文的切换?
- 无锁并发编程:可以使用一些方法避免使用锁,如将数据的ID按照Hash算法取模分段,不同的线程处理不同段的数据。
- CAS算法:Java的Atomic包使用CAS算法更新数据,而不需要加锁。
- 使用最少线程:避免创建不需要的线程。
- 协程:在单线程里实现多任务的调度,并在单线程里维持多个任务间的切换。
挑战二:死锁
public class DeadLockDemo { private static String A = "A"; private static String B = "B"; public static void main(String[] args) { new DeadLockDemo().deadLock(); } private void deadLock(){ Thread thread1 = new Thread(new Runnable() { @Override public void run() { synchronized (A){ try { Thread.sleep(2000); } catch (InterruptedException e){ e.printStackTrace(); } synchronized (B){ System.out.println("1"); } } } }); Thread thread2 = new Thread(new Runnable() { @Override public void run() { synchronized (B){ synchronized (A){ System.out.println("2"); } } } }); thread1.start(); thread2.start(); } }
避免死锁的几个常见方法:
- 避免一个线程同时获取多个锁
- 避免一个线程在锁内同时占用多个资源,尽量保证每个锁只占一个资源
- 尝试使用定时锁,使用lock.tryLock(timeout)来替代使用内部锁机制
- 对于数据库锁,加锁和解锁必须在一个数据库连接里
挑战三:资源限制的挑战
(1) 什么是资源限制?
指在进行并发编程时,程序的执行速度受限于计算机硬件资源或软件资源。
(2) 资源限制引发的问题?
在并发编程中,将代码执行速度加快的原则是将代码中串行的部分变成并发执行,但是如果将某段串行的代码并发执行,因为受限于资源,仍然在串行执行,这时候程序不仅不会加快执行,反而会更慢,因为增加了上下文切换和资源调度的时间。
(3)如何解决资源限制的问题?
对于硬件限制,可以考虑集群并行执行程序。既然单机的资源有限制,就让程序在多机上运行。比如使用ODPS、Hadoop或者自己搭建服务器集群;
对于软件限制,可以考虑使用资源池将资源复用。比如使用连接池将数据库和Socket连接复用,或者在调用对方webService接口获取数据时,只建立一个连接。
(4)如何在资源限制的情况下进行并发编程?
根据不同的资源限制调整程序的并发度,比如下载文件程序依赖于两个资源——宽带和硬盘读写速度。有数据库操作时,设计数据库连接数,如果SQL语句执行非常快,而线程的数量比数据库连接数大很多,则某些线程会被阻塞,等待数据库连接。
不积跬步,无以至千里;不积小流,无以成江海
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· Docker 太简单,K8s 太复杂?w7panel 让容器管理更轻松!