对于多线程场景下的计数操作,应该使用AtomicInteger
或AtomicLong
保证线程安全。不能使用i++。
volatile
关键字可以保证线程之间的可见性,但是无法保证操作的原子性。对于volatile i = 0; i++
操作,需要加锁或使用CAS操作来保证原子性。
Object.wait
和Object.notify
方法必须在synchronized
关键字修饰的代码块中使用。因为wait和notify的语义是:线程在获取到锁之后暂时释放锁(wait)或通知(notify)其他线程可以竞争锁了。所以必须使用synchronized
关键字修饰。
对于中断异常InterruptedException
,不能忽略,需要积极地响应中断,释放资源并准备退出线程。参考# 聊聊JDK推荐的线程关闭方式
Thread.interrupted
方法用于返回当前线程的中断状态,并且在此过程中会清除中断状态。如果只想查询中断状态而不清除中断状态,可以使用 Thread.isInterrupted
方法。
在使用 new Thread()
创建线程对象时,只是在 Java 中创建了一个对象。执行start
方法才真正在操作系统中创建线程并开始执行线程。Java 的线程是基于操作系统内核级线程实现的,而不是虚拟线程。
避免使用 Thread.stop 和 Thread.resume
方法停止线程,因为它们已经被标记为不推荐使用的方法。关于如何优雅地终止线程,请参考 # 聊聊JDK推荐的线程关闭方式
ThreadPoolExecutor
在达到CoreSize
线程数时,会将请求放入队列中,如果队列已满,则尝试增加线程数到MaxSize。因此,无界队列永远无法达到MaxSize,并且永远不会触发拒绝策略,但存在OOM的风险。
使用Executors.newFixedThreadPool
创建固定线程数的线程池时,使用无界队列。在极端情况下,可能导致OOM。因此,在使用此API创建线程池时要谨慎。
ThreadPoolExecutor
线程池的默认拒绝策略是抛出异常,这会导致请求处理失败。可以使用CallerRunsPolicy
策略,让提交线程处理任务,以避免请求处理失败的情况发生。
ExecutorService的execute
方法是同步提交任务的,而submit方法是异步提交任务的。
synchronized
是非公平模式的锁。如果需要使用公平模式的锁,可以使用ReentrantLock
。公平模式下,先申请锁的先获取锁;非公平模式下,申请锁时会先尝试CAS加锁,如果加锁失败则排队等待获取锁,这样提高了锁切换的速度,但失去了公平性。
synchronized
只支持阻塞模式的锁申请。如果需要非阻塞模式,请使用ReentrantLock
。
在使用 CountDownLatch
时,即使是在异常场景下,也要确保进行 countDown
操作,否则等待线程的 await 方法可能永远无法被唤醒。建议 countDown
操作放到 finally 代码块中。
Thread.sleep
是一个用于将当前线程置为阻塞状态的方法,它会暂时让出CPU的调度权,直到指定的时间到达或者被Thread.interrupt()
方法中断。相比之下,Thread.yield
方法仅仅是让出CPU的调度权,但操作系统下一次调度时会正常考虑该线程。另一方面,Object.wait()
方法通常用于在synchronized
同步代码块中,它会暂时释放锁,并等待其他持有该锁的线程来唤醒它。在此期间,线程进入阻塞状态。Thread.join
是当前线程等待子线程运行结束才能继续执行
一个线程发生OOM ,只会导致这个线程抛出ERRO,进行退出执行。不会影响其他线程。如果想要在OOM后,退出进程,需要添加JVM 启动参数。【 -XX:+HeapDumpOnOutOfMemoryError
参数表示当JVM发生OOM时,自动生成DUMP文件】;【-XX:+ExitOnOutOfMemoryError
在程序发生OOM异常时,强制退出】; 【XX:+CrashOnOutOfMemoryError
在程序发生OOM异常时,强制退出,并生成Crash日志】;