专栏名称: 亿级流量网站架构
开涛技术点滴
目录
相关文章推荐
OSC开源社区  ·  100%国产AI新成员:壁仞科技成功适配De ... ·  2 天前  
OSC开源社区  ·  谷歌安卓系统“假开源、真垄断”? ·  2 天前  
OSC开源社区  ·  漫谈DeepSeek及其背后的核心技术 ·  昨天  
程序员小灰  ·  这款AI编程工具,将会取代Cursor! ·  4 天前  
51好读  ›  专栏  ›  亿级流量网站架构

你的Java代码可中断吗(2)

亿级流量网站架构  · 公众号  · 程序员  · 2017-06-01 08:05

正文

承接《 你的Java代码可中断吗 》,下一篇《线程中断、超时与降级》。


1.确保提交到线程池的任务可中断

原文:www.securecoding.cert.org,TPS02-J. Ensure thattasks submitted to a thread pool are interruptible。

为了能完全关闭线程池或者取消线程池中的个别任务,程序应提交支持使用Thread.interrupt()中断的任务到线程池。程序不应提交不支持中断的任务到线程池,除非是无阻塞且短时间运行的任务。

根据 java.util.concurrent.ExecutorService.shutdownNow() Java API的描述:

尝试终止所有正在执行的任务,停止所有等待处理的任务,并且返回等待执行的任务列表…


除了尽最大努力尝试终止正在执行的任务之外,没有任何其他的保证。例如,典型实现是通过Thread.interrupt()进行取消,因此任何不能中断的任务可能永远不会被终止。

违规代码示例(Shutting Down Thread Pools)

如下是违规代码示例,提交SocketReader任务到PoolService声明的线程池中:

因为任务不支持Thread.interrupt()进行中断,且因为shutdown()方法必须等到所有正执行的任务执行完成,所以shutdownNow()方法可能不能立即完全关闭线程池。

同样,某些不用Thread.interrupted() 而使用其他的机制来决定何时关闭的任务,其是无法响应shutdown()和shutdownNow()。例如,通过检查一个volatile标志位来决定是否可安全关闭的任务,对这些方法是无法响应的。 THI05-J. Do not use Thread.stop() to terminate threads 提供了使用标志位终止线程的更多信息。

合规解决方案(Submit InterruptibleTasks)

如下合规解决方案定义了SocketReader的一个可中断的版本,实例化并提交到线程池:

2.持有锁时不要执行阻塞操作

原文:www.securecoding.cert.org,LCK09-J.Do not perform operations that canblock while holding a lock


持有锁时执行耗时或阻塞操作会严重降低系统性能,且可能导致饥饿。此外,死锁会导致相互依赖的线程无限阻塞。阻塞操作包括网络、文件、控制台I/O(如Console.readLine())和对象序列化,无限延迟线程执行也算是一种阻塞操作(如通过Thread.sleep())。因此,在持有锁时,程序不应该执行阻塞操作。

当Java虚拟机(JVM)与不可靠网络、文件I/O交互时,可能引起巨大的性能损耗。在这种情况下,应避免持有锁时执行网络上的文件I/O操作。在等待输出流锁或I/O完成事件的文件操作(如日志)会阻塞,可以在一个单独的线程中执行来加速任务处理。记录请求日志可以添加到一个队列,与直接文件I/O相比,队列put()操作会增加一点小的开销。

违规代码示例(Deferring a Thread)

如下违规代码示例定义了一个接受timeout参数的工具类:

方法是synchronized,当线程休眠后其他线程不能使用synchronized的方法。当前对象的监视器并未被释放,这是因为Thread.sleep() 方法没有同步语义。

合规解决方案(Intrinsic Lock)

如下合规解决方案定义了doSomething()方法,其带有一个timeout参数而不是time参数。使用Object.wait()代替Thread.sleep(),Object.wait()允许设置一个通知的超时周期,超时后可以唤醒线程。

进入到等待状态后当前对象的监视器会立即释放。当超时后,线程会在重新获取到当前对象的监视器后恢复继续执行。

根据Object类Java API的描述:

wait方法,它将当前线程放入到该对象的等待集,且只有该对象能释放锁;当线程等待时,当前线程上的任何synchronized的其他对象将保持锁定。


此方法应仅由该对象监视器拥有者线程调用。

程序必须确保持有其他对象锁的线程在进入等待状态之前适时释放这些锁。等待和通知的一些额外帮助可见 THI03-J. Always invoke wait() and await() methods insidea loop 和 THI02-J. Notify all waiting threads rather than a single thread。


违规代码示例(Network I/O)

如下违规代码示例定义了sendPage()方法,其从服务端向客户端发送一个Page对象。该方法是synchronized,在多线程请求并发访问时来来保护 pageBuff数组。







请到「今天看啥」查看全文