多线程与高并发
本文系统总结了Java多线程编程的核心知识点。主要内容包括:1)线程创建方式(继承Thread、实现Runnable/Callable、线程池);2)线程同步机制(synchronized原理、锁优化、CAS、ReentrantLock);3)线程通信(wait/notify、LockSupport);4)线程池原理与调优;5)并发工具(CountDownLatch、Semaphore);6)Th
为什么要使用多线程
多线程能够提高程序的并发性和响应性。通过多线程可以充分利用多核CPU的计算能力,提高程序的执行效率。多线程适用于需要同时处理多个任务的场景,如网络请求、文件IO、GUI界面更新等。
创建线程有几种方式
定义Thread类的子类,并重写该类的run方法
class MyThread extends Thread {
@Override
public void run() {
System.out.println("Thread running");
}
}
定义Runnable接口的实现类,并重写该接口的run()方法
class MyRunnable implements Runnable {
@Override
public void run() {
System.out.println("Runnable running");
}
}
定义Callable接口的实现类,并重写该接口的call()方法
class MyCallable implements Callable<String> {
@Override
public String call() throws Exception {
return "Callable result";
}
}
线程池的方式
ExecutorService executor = Executors.newFixedThreadPool(5);
executor.submit(() -> System.out.println("Task executed"));
start()方法和run()方法的区别
start()方法会启动一个新线程,并在新线程中执行run()方法。run()方法只是普通方法调用,不会创建新线程。
线程和进程的区别
进程是操作系统分配资源的基本单位,线程是CPU调度的基本单位。一个进程可以包含多个线程,线程共享进程的资源,但拥有独立的执行栈和程序计数器。
Runnable和Callable有什么区别
Runnable的run()方法没有返回值,也不能抛出异常。Callable的call()方法可以返回结果并抛出异常,通常与Future配合使用。
volatile作用,原理
volatile保证变量的可见性和禁止指令重排序。原理是通过内存屏障强制将变量的修改立即写入主内存,并使其他线程的缓存失效。
并发与并行的区别
并发是多个任务交替执行,并行是多个任务同时执行。并发适用于单核CPU,并行需要多核CPU支持。
synchronized的实现原理以及锁优化
synchronized通过monitorenter和monitorexit指令实现,锁优化包括偏向锁、轻量级锁、重量级锁和自旋锁。
monitorenter、monitorexit、ACC_SYNCHRONIZED
monitorenter和monitorexit用于同步代码块,ACC_SYNCHRONIZED用于同步方法。
monitor监视器
每个对象都有一个关联的monitor,用于控制线程的访问。
Java Monitor的工作机理
线程通过获取monitor的锁来进入同步代码块,其他线程必须等待锁释放。
对象与monitor关联
对象的Mark Word中存储了指向monitor的指针。
线程有哪些状态
线程状态包括NEW、RUNNABLE、BLOCKED、WAITING、TIMED_WAITING和TERMINATED。
synchronized和ReentrantLock的区别
synchronized是关键字,ReentrantLock是类。ReentrantLock提供更灵活的锁机制,如公平锁、可中断锁和超时锁。
wait(),notify()和suspend(),resume()之间的区别
wait()和notify()是Object的方法,需要配合synchronized使用。suspend()和resume()已废弃,容易导致死锁。
CAS?CAS有什么缺陷,如何解决
CAS(Compare-And-Swap)是一种无锁算法,缺陷包括ABA问题和循环时间长开销大。ABA问题可以通过版本号解决。
CountDownLatch与CyclicBarrier区别
CountDownLatch是一次性的,CyclicBarrier可重复使用。CountDownLatch等待事件,CyclicBarrier等待线程。
什么是多线程环境下的伪共享
伪共享是由于多个线程频繁修改同一缓存行的不同变量,导致缓存失效。可以通过填充或对齐解决。
如何解决伪共享问题
使用@Contended注解或手动填充字段。
Fork/Join框架的理解
Fork/Join框架用于并行执行任务,采用工作窃取算法提高效率。
ThreadLocal原理
ThreadLocal为每个线程提供独立的变量副本,通过ThreadLocalMap存储数据。
TreadLocal为什么会导致内存泄漏
ThreadLocalMap的key是弱引用,但value是强引用。如果线程长时间运行,value不会被回收。
弱引用导致的内存泄漏
key被回收后,value仍然存在。
key是弱引用,GC回收会影响ThreadLocal的正常工作嘛
不会,ThreadLocal本身是强引用。
ThreadLocal内存泄漏的demo
ThreadLocal<Object> tl = new ThreadLocal<>();
tl.set(new Object());
tl = null; // key被回收,value仍在
为什么ThreadLocalMap的key是弱引用,设计理念是
避免ThreadLocal对象无法被回收,但需手动清理value。
如何保证父子线程间的共享ThreadLocal数据
使用InheritableThreadLocal,子线程会继承父线程的ThreadLocal值。
如何保证多线程下i++结果正确
使用AtomicInteger或synchronized。
如何检测死锁?怎么预防死锁?死锁四个必要条件
检测死锁使用jstack或ThreadMXBean。预防死锁通过破坏互斥、占有且等待、不可抢占和循环等待条件。
如果线程过多,会怎样
线程过多会导致上下文切换频繁,系统资源耗尽,性能下降。
happens-before原则
happens-before原则定义了操作之间的可见性关系,如程序顺序规则、锁规则等。
如何实现两个线程间共享数据
使用共享变量、volatile、synchronized或并发容器。
LockSupport作用是
LockSupport提供线程阻塞和唤醒的基本操作,比wait()/notify()更灵活。
线程池如何调优,如何确认最佳线程数
最佳线程数取决于任务类型和系统资源,通常为CPU核心数乘以期望的CPU利用率。
为什么要用线程池
线程池减少线程创建和销毁的开销,提高资源利用率和管理便捷性。
Java的线程池执行原理
线程池通过核心线程、任务队列和最大线程数控制任务执行。
聊聊线程池的核心参数
核心线程数、最大线程数、空闲时间、任务队列和拒绝策略。
当提交新任务时,异常如何处理
通过Future.get()捕获异常或设置UncaughtExceptionHandler。
AQS组件,实现原理
AQS(AbstractQueuedSynchronizer)是并发包的基础,通过state和CLH队列实现同步。
state状态的维护
通过CAS操作修改state。
CLH队列
双向队列管理等待线程。
ConditionObject通知
实现条件变量,支持await()和signal()。
模板方法设计模式
子类实现tryAcquire和tryRelease。
独占与共享模式
独占锁如ReentrantLock,共享锁如Semaphore。
自定义同步器
通过继承AQS实现自定义锁。
Semaphore原理
Semaphore通过AQS实现,控制同时访问资源的线程数。
Semaphore使用demo
Semaphore semaphore = new Semaphore(3);
semaphore.acquire();
semaphore.release();
Semaphore原理
通过state表示可用许可数。
synchronized做了哪些优化?什么是偏向锁?什么是自旋锁?锁粗化?
偏向锁减少无竞争时的开销,自旋锁避免线程切换,锁粗化合并连续锁操作。
什么是上下文切换?什么是CPU上下文?什么是CPU上下文切换?
CPU上下文是线程的执行状态,上下文切换是保存和恢复线程状态的过程。
为什么wait(),notify(),notifyAll()在Object中,而不在Thread类中
因为这些方法需要操作对象的监视器锁,与对象关联。
线程池中submit()和execute()方法有什么区别
submit()返回Future,可以获取结果或异常;execute()没有返回值。
AtomicInteger的原理
通过CAS操作保证原子性。
Java中用到的线程调度算法是什么
时间片轮转和优先级调度。
shutdown()和shutdownNow()的区别
shutdown()平滑关闭,shutdownNow()立即中断所有线程。
说说几种常见的线程池及使用场景
newFixedThreadPool
固定大小,适用于负载稳定的场景。
newCachedThreadPool
弹性大小,适用于短期异步任务。
newSingleThreadExecutor
单线程,适用于任务顺序执行。
newScheduledThreadPool
定时任务,适用于周期性执行。
什么是FutureTask
FutureTask是可取消的异步计算任务,实现Runnable和Future。
java中interrupt(),interrupted()和isInterrupted()的区别
interrupt()中断线程,interrupted()检查并清除中断状态,isInterrupted()仅检查状态。
有三个线程T1,T2,T3,怎么确保它们按顺序执行
使用join()或CountDownLatch。
有哪些阻塞队列
ArrayBlockingQueue、LinkedBlockingQueue、PriorityBlockingQueue等。
Java中ConcurrentHashMap的并发度是什么
并发度是分段锁的数量,影响并发性能。
Java线程有哪些常用的调度方法
线程休眠
Thread.sleep()
线程中断
Thread.interrupt()
线程等待
Object.wait()
线程让步
Thread.yield()
线程通知
Object.notify()
ReentrantLock的加锁原理
ReentrantLock使用的模板
lock.lock();
try {
// critical section
} finally {
lock.unlock();
}
什么是非公平锁,什么是公平锁?
非公平锁允许插队,公平锁按顺序获取锁。
lock()加锁流程
通过AQS的tryAcquire实现。
线程间的通讯方式
volatile和synchronized关键字
保证可见性和同步。
等待/通知机制
wait()和notify()。
管道输入/输出流
PipedInputStream和PipedOutputStream。
join()方法
等待线程结束。
ThreadLocal
线程局部变量。
写出3条你遵循的多线程最佳实践
- 使用线程池管理线程。
- 避免共享可变状态,使用不可变对象或线程安全容器。
- 优先使用高级并发工具如
ConcurrentHashMap和CountDownLatch。
为什么阿里发布的Java开发手册中强制线程池不允许使用Executors去创建
Executors提供的默认线程池可能导致资源耗尽,如newFixedThreadPool和newCachedThreadPool的队列无界或线程数无界。推荐手动配置ThreadPoolExecutor。
更多推荐



所有评论(0)