📖 English Documentation | 📖 中文文档
👉 通过Demo演示出Java中并发问题。
- 可以观察到的实际现象 🙈 比 说说的并发原则 🙊 更直观更可信。
Java语言标准库支持线程,语言本身(如GC)以及应用(服务器端the server side)中会重度使用多线程。- 并发程序设计在分析和实现中,复杂度大大增加。 如果不充分理解和系统分析并发逻辑,随意写代码,这样的程序用 『碰巧』 能运行出正确结果 来形容一点都不为过。
这里的Demo没有给出解释和讨论,并且都是入门级的
,更多了解请参见一些并发的问题讨论和资料。
你在开发中碰到的并发问题的例子,欢迎提供(提交Issue)和分享(Fork后提交代码)! 😘
- 🍺 无同步的修改在另一个线程中会读不到
- 🍺
HashMap的死循环 - 🍺 组合状态读到无效组合
- 🍺
long变量读到无效值 - 🍺 无同步的并发计数结果不对
- 🍺 在易变域上的同步
- 🍺 对称锁死锁
- 🍺 指令重排序导致非final域变量读取错误
- 🍺 线程池循环引用死锁
- 一些并发的问题讨论和资料
Demo类NoPublishDemo。
主线程中设置属性stop为true,以控制在main启动的任务线程退出。
在主线程属性stop为true后,但任务线程持续运行,即任务线程中一直没有读到新值。
./mvnw compile exec:java -Dexec.mainClass=fucking.concurrency.demo.NoPublishDemo这个问题在疫苗:Java HashMap的死循环等多个地方都有讲解。
Demo类HashMapHangDemo,可以复现这个问题。
主线程中开启2个任务线程执行HashMap的put操作。主线程做get操作。
通过没有持续的输出判定主线程Block,即HashMap的出现死循环。
./mvnw compile exec:java -Dexec.mainClass=fucking.concurrency.demo.HashMapHangDemo程序设计时,会需要多个状态记录(状态可以是个POJO对象或是int等等)。常看到多状态读写没有同步的代码,并且写的同学会很自然地就忽略了线程安全的问题。
无效组合 是指 从来没有设置过的组合。
主线程修改多个状态,为了方便检查,每次写入有个固定的关系:第2个状态是第1个状态值的2倍。在任务线程中读取多个状态。
Demo类InvalidCombinationStateDemo。
任务线程中读到了 第2个状态不是第1个状态值2倍的值,即是无效值。
./mvnw compile exec:java -Dexec.mainClass=fucking.concurrency.demo.InvalidCombinationStateDemo无效值 是指 从来没有设置过的值。
long变量读写不是原子的,会分为2次4字节操作。
Demo类InvalidLongDemo。
主线程修改long变量,为了方便检查,每次写入的long值的高4字节和低4字节是一样的。在任务线程中读取long变量。
任务线程中读到了高4字节和低4字节不一样的long变量,即是无效值。
./mvnw compile exec:java -Dexec.mainClass=fucking.concurrency.demo.InvalidLongDemoDemo类WrongCounterDemo。
主线程中开启2个任务线程执行并发递增计数。主线程最终结果检查。
计数值不对。
./mvnw compile exec:java -Dexec.mainClass=fucking.concurrency.demo.WrongCounterDemo常看到在易变域上的同步代码,并且写的同学会很自然觉得这样是安全和正确的。
# 问题分析见文章链接:在易变域上的同步,对应的英文文章:Synchronization on mutable fields
Demo类SynchronizationOnMutableFieldDemo。
主线程中开启2个任务线程执行addListener。主线程最终结果检查。
最终Listener的个数不对。
./mvnw compile exec:java -Dexec.mainClass=fucking.concurrency.demo.SynchronizationOnMutableFieldDemo# 问题分析见文章链接:对称锁死锁,对应的英文文章:Synchronization on mutable fields
Demo类SymmetricLockDeadlockDemo。
主线程中开启2个任务线程执行。
任务线程死锁。
./mvnw compile exec:java -Dexec.mainClass=fucking.concurrency.demo.SymmetricLockDeadlockDemoDemo类FinalInitialDemo。
writer线程调用类的构造函数,reader线程获取类的非final的成员变量。
调用构造函数时,可能会发生指令重新排序,将非final域变量放置在构造函数之外,导致writer和reader线程获取变量的默认初始值(指令顺序不一定发生, 并且需要特定的硬件和 JVM 环境)。
./mvnw compile exec:java -Dexec.mainClass=fucking.concurrency.demo.FinalInitialDemoDemo类CyclicThreadPoolDeadLockDemo。
该示例展示了在使用线程池时,由于任务间的循环依赖线程池导致死锁的问题,以及如何通过CompletableFuture来避免这种情况。
在badCase中,两个线程池pool1和pool2相互提交任务,形成循环依赖。当线程池的线程数耗尽时,所有执行中的任务都在等待其他任务完成,导致死锁。 goodCase通过使用CompletableFuture的异步链式调用,避免了线程池的阻塞,从而解决了死锁问题。
./mvnw compile exec:java -Dexec.mainClass=fucking.concurrency.demo.CyclicThreadPoolDeadLockDemo- ibm developerworks - 多核系统上的
Java并发缺陷模式(bug patterns) - stackoverflow - What is the most frequent concurrency issue you've encountered in Java?
- Java Concurrency Gotchas
- Common problems of concurrency (Multi-Threading) in Java
- java tutorial - Concurrency
要深入了解请参见并发方面的系统的资料。
