Java线程从入门到实践
Java线程杂谈:从入门到“放弃”?
大家好,今天我们来聊聊Java线程这个让人又爱又恨的话题。作为一个Java开发者,谁没在深夜里对着死锁日志抓狂过?谁没被
ConcurrentModificationException折磨得怀疑人生?今天我们就用轻松的方式,聊聊线程那些事儿。
一、线程的诞生:是继承Thread还是实现Runnable?
新手必问:"创建线程到底用继承Thread还是实现Runnable?"
答案很简单:优先选Runnable!为什么?
- Java单继承的枷锁:继承Thread后你的类就不能继承其他爹了
- 资源共享优势:多个线程可以共享同一个Runnable实例
- Lambda的魔法:new Thread(() -> System.out.println("真香")).start();
但面试官总爱追问:"那Callable和Future呢?"
悄悄告诉你:当需要返回值或抛异常时,它们才是真香组合!
二、线程状态:比女朋友的心情还复杂
线程的6种状态(别数了,Java定义的就是6种):
- NEW:刚new出来还没start的萌新
- RUNNABLE:在JVM眼里,就绪和运行都算runnable
- BLOCKED:拿着爱的号码牌等锁的苦主
- WAITING:望眼欲穿等唤醒的痴情种(wait/join)
- TIMED_WAITING:设了闹钟的等待(sleep(1000))
- TERMINATED:领盒饭退场
经典面试题:"sleep()和wait()的区别?"
- sleep是Thread的静态方法,不释放锁
- wait是Object的方法,释放锁还要被notify
三、同步的艺术:synchronized的千层套路
java
// 对象锁的三种姿势
public synchronized void method() {} // 等同于锁this
public void method() { synchronized(this) {} }
public void method() { synchronized(obj) {} }
// 类锁的正确打开方式
public static synchronized void staticMethod() {} // 锁Class对象
但synchronized太重了怎么办?
试试ReentrantLock的三大绝招:
- 可中断等待:lock.lockInterruptibly()
- 公平锁:new ReentrantLock(true)
- 条件变量:Condition condition = lock.newCondition()
四、线程池:别重复造轮子!
为什么要用线程池?看看这血泪史:
- 频繁创建/销毁线程开销大
- 不加管控的线程可能OOM
- 缺乏统一管理(拒绝策略、监控等)
Executor框架的四大天王:
java
复制
Executors.newCachedThreadPool(); // 线程数弹性伸缩
Executors.newFixedThreadPool(8); // 固定大小
Executors.newSingleThreadExecutor(); // 单线程串行执行
Executors.newScheduledThreadPool(3); // 定时任务专家
但阿里规约为什么不让直接用Executors?
因为默认队列无界可能OOM!推荐手动newThreadPoolExecutor,七参数配置更灵活。
五、并发工具包:JUC大法好
- CountDownLatch:老板等所有员工下班锁门
java
CountDownLatch latch = new CountDownLatch(3);
// 三个线程分别countDown()
latch.await(); // 阻塞直到计数器归零
- CyclicBarrier:旅游团集合点等人齐发车
java
CyclicBarrier barrier = new CyclicBarrier(5, ()->System.out.println("发车!"));
- Semaphore:海底捞等位区的叫号机
java
Semaphore semaphore = new Semaphore(5); // 5个许可
semaphore.acquire(); // 获取许可
semaphore.release();
- ConcurrentHashMap:线程安全的哈希表(比Hashtable高到不知哪里去了)
六、那些年我们踩过的坑
- 死锁现场:
O 必要条件:互斥、占有且等待、不可抢占、循环等待
O 破局方法:jstack查线程栈,VisualVM在线监控
- 内存可见性:
O volatile解决可见性问题(但不保证原子性)
O 经典案例:while(!stopFlag)可能永远不停止
- ThreadLocal的魔幻现实:
O 每个线程独立副本
O 但用完要remove()!否则线程池复用会导致数据错乱
七、新世界大门:Java 8+的并发黑科技
- CompletableFuture:异步编程利器
java
CompletableFuture.supplyAsync(() -> "hello")
.thenApply(s -> s + " world")
.thenAccept(System.out::println);
- 并行流(Parallel Stream):一行代码实现并行处理
java
IntStream.range(1,100).parallel().sum();
- StampedLock:读写锁的升级版,乐观读锁提升性能
结语:多线程修炼指南
想要真正掌握Java并发,建议:
- 精读《Java并发编程实战》(虽然可能看三遍才懂)
- 多写demo,用jstack、VisualVM等工具分析
- 参与开源项目,看别人怎么写并发代码
- 牢记:没有银弹! 不同场景选择不同方案
最后送大家一句保命箴言:"能不用锁就别用,要用就用最小粒度的锁!"
互动话题:你遇到过最诡异的并发bug是什么?欢迎在评论区分享你的血泪史!