我们都知道在java软件开发领域,多线程这一关键词从来就不会缺席,那么你知道java多线程的使用场景有哪些吗?都用来做什么呢?
Thread。第一类就是Thread类了。大家都知道有线程有两种实现方式。第一可以继承Thread覆盖它的run方法;第二种是实现Runnable接口,实现它的run方法;而第三种创建线程的方法,就是通过线程池。那么具体代码实现,就放在run方法中。
一般会关注两种情况。一个是线程退出的条件,一个就是异常处理情况。
线程退出。有的run方法执行完成之后,线程就会退出了。但有的run方法是永远不会结束的。结束一个线程肯定不是通过Thread.stop()方法的,这个方法早已经在java1.2版本就被废弃了。所以大体会有两种方式控制线程。
定义退出标志放在while中
代码一般是这样的。
private volatile boolean flag = true; public void run() { while (flag) {} }
标志呢一般使用volatile进行修饰,使其可读,然后再通过设置这个值来控制线程的运行,这就已经成了约定俗成的套路。
使用interrupt方法终止线程。类似下面这种。
while(!isInterrupted()){……}
对于InterruptedException,比如Thread.sleep所抛出的,我们一般是去补获它,然后再静悄悄的忽略掉。中断允许一个可取消任务来清理正在进行的工作,然后通知其他任务它要被取消,最后才终止,在这种情况下,该类异常需要被仔细处理。
interrupt方法是不一定会真正”中断”线程的,它只是一种协作机制。interrupt方法通常不能中断一些处于阻塞状态的I/O操作。比如写文件,或者socket传输等。这种情况,需要同时调用正在阻塞操作的close方法,才能够正常的退出。
interrupt系列使用时候一定要注意,会引入bug,甚至死锁。
异常处理。一般java中会抛出两种异常。一种是必须要捕获的,比如InterruptedException,否则无法通过编译;另外一种是可以处理也可以不处理的,比如NullPointerException等等。
那么在我们的任务运行中,很有可能抛出这两种异常。对于第一种异常,是必须放在try,catch中的。但第二种异常如果不去处理的话,是会影响任务的正常运行的。
有很多时候在处理循环的任务时,没有捕获一些隐式的异常,造成任务在遇到异常的情况下,并不能继续执行下去。如果不能确定异常的种类,可以直接捕获Exception或者更通用的Throwable。
while (!isInterrupted()) { try { ……} catch (Exception ex) { ……} }
同步方式。java中实现同步的方式有很多,大体分为以下几种。
synchronized 关键字,wait、notify等,Concurrent包中的ReentrantLock,volatile关键字,ThreadLocal局部变量。
生产者、消费者是wait、notify最典型的应用场景,这些函数的调用,是必须要放在synchronized代码块里才能够正常运行的。
信号量。Semaphore虽然有一些应用场景,但大部分属于炫技的,在编码中应该尽量少用。
信号量可以实现限流的功能,但它只是常用限流方式的一种。其他两种是漏桶算法、令牌桶算法。
hystrix的熔断功能,也有使用信号量进行资源的控制。
End。不管是wait、notify,还是同步关键字或者锁,能不用就不要用,毕竟它们会引发程序的复杂性。最好的方式就是,是直接使用concurrent包所提供的机制,来规避一些编码方面的问题。
concurrent包中的CAS概念,在一定程度上算是无锁的一种实现。更专业的有类似disruptor的无锁队列框架,但它依然是建立在CAS的编程模型上的。近些年,类似AKKA这样的事件驱动模型正在走红,但编程模型简单,不代表实现简单,背后的工作依然需要多线程去协调。
golang引入协程(coroutine)概念以后,对多线程加入了更加轻量级的补充。java中可以通过javaagent技术加载quasar补充一些功能,但我觉得你不会为了这丁点效率去牺牲编码的可读性。
那么以上就是有关java多线程的所有内容了,还想了解更多java架构师的相关信息,记得马上来关注本站消息哦。