为什么java的线程和协程不能直接中断,而协程可以

博客分类:
* In live var analysis a BB asks its successor (in essence) about which
* vars are live, mixes it with its own uses and defs and passes on a
* new list of live vars to its predecessors. Since the information
* bubbles up the chain, we iterate the list in reverse o ...
博客分类:
小于1.5编译级别时,如果不显示inline try/catch/finally块,try/catch和any会产生的jsr指令跳转到finally。此处分析kilim如何对这种情况下的jsr指令进行内联:finally块中有pausable则会被拷贝一份;finally块中有pausable则会把jsr/ret指令都替换为goto指令,但是并没有像1.5及以后编译级别那样,拷贝一份代码try/catch代码块编译后的指令块中。下面看下analyze中调用的kilim.analysis.MethodFlow.inlineSubroutines()的实现:
privat ...
博客分类:
上一篇分析了可织入判断的代码,本篇继续分析织入部分的构造/合并BasicBlock。
首先看下分析方法kilim.analysis.MethodFlow.analyze()包含的功能:
//织入逻辑
public void analyze() throws KilimException {
buildBasic ...
博客分类:
1、织入入口,配置
1.1、织入入口
kilim.tools.Weaver是织入的主类,通过程序参数设置要织入的代码路径,可以是class文件、jar包、其他(是什么)、目录(目录中可以是jar包、class文件)。
如果传入的是class文件,会直接织入;调用kilim.tools.Weaver.weaveFile(String, InputStream, Detector)织入
如果是jar包,会解析jar文件,for循环时候,jdk会把for编译成iterator接口实现,对于jar文件,实际提取文件内容的是kilim. ...
博客分类:
java语言处理多任务的模式是基于多线程,java语言级别原生并不支持协程,我们想要java语言支持协程,就需要在线程和协程之间架起一道桥梁。在某个事件点(我们成为挂起点)上,我们在应用级别备份当前任务在线程上的调用栈信息(包括局部变量和操作栈上的数据),释放线程,让它去执行下一个任务;等某些事件被触发的时候,重新执行刚才的任务,用之前备份的调用栈信息恢复线程的调用栈,从挂起点开始执行。
来看看Kilim是如何实现标示出挂起点、识别出需要备份的调用栈、备份/恢复调用栈、任务的唤醒这些功能的:
1、标识挂起点,kilim框架通过
kilim.Task.pause(kilim.Pause ...
yueyemaitian
浏览: 226680 次
来自: 杭州
很不错,学习了
你这么牛逼,你父母知道吗
谢楼主,构建成功了
?????
(window.slotbydup=window.slotbydup || []).push({
id: '4773203',
container: s,
size: '200,200',
display: 'inlay-fix'协程:异步与并发
发表于 日 11:27 | Hits: 3447
协程(coroutine)的概念已经广为人知,这里就不多说了。作为用户态主动调度的执行单位,协程可以避免传统多线程程序的上下文切换、调度和锁竞争等开销。
前一段时间一个小师弟过来面试,提到在阿里实习时,为了方便的编写高并发长连接程序,调研发现了了一个java的协程实现。我于是便问他结果如何,用这个真的能够达到你的目的吗?他就答不出来了,只是在机械的重复「协程相比回调更适合异步编程」之类的。
很多人都有这个误解,认为有了协程,就可以用同步程序的方式,写出异步的程序,原先同步的程序和第三方库,也会自动变成异步的。为什么说这是个误解呢,因为要写出有异步效果的程序,只有协程是不够的,还需要有底层IO的支持。在发生IO时,要将IO操作交给异步实现去执行,并让渡出协程的执行权,由调度去调度执行其他协程。
事实上,协程只是用户态实现的一种调度方法而已,除去天生支持协程(或协程的类似物)的lua, stackless-python, golang等之外,一个本不支持协程语言要实现协程是很容易的事情。事实上,基本上所有的的语言都有对协程支持的第三方实现。
问题是,这些语言的底层IO实现,并未对协程调用做处理,其结果是仍然会阻塞这个协程,并没有实现异步的效果。而常用的http /数据库/缓存等lib 都是基于这些语言的底层IO库实现的(如httpclient/libcurl/mysql-connector等),所以使用了这些库的话,在IO 操作时不会让渡执行权,在当前协程阻塞在IO操作上的时候,其他协程也完全无法执行,这比多线程的实现还要糟糕。
所以,java,c#,c++这样的语言,虽然有协程的实现,但是使用起来却基本上没有意义。没有底层IO实现的支持,协程也就只能用来谢谢生产者-消费者这样的demo而已,除非协程本身提供新的支持协程的IO 底层库并实现所需要的http / database / cache 等的lib。
lua,golang等在其系统的底层IO实现已经做了处理,所以才可以使用协程。即使如此,依然有需要注意的地方。比如:
并非所有的IO操作都一定是异步的。例如golang只对socket IO有异步实现,文件IO、Pipe、终端输入输出等依然是阻塞的,所以在goroutine要特别注意这些操作。
协程只有在发生IO的时候才会让渡执行权,因此存在调度公平性的问题。在大量cpu操作、没有IO的情况下,当前协程会一直占用执行,其他协程得不到执行的机会。所以协程要注意不要有大量密集CPU操作。
关于Python
Python的IO实现虽然也没有对协程做处理,但是Python这样的动态语言,可以使用monkey patch的方式替换系统的IO实现。gevent就是这种做法,这也是使用gevent的greenlet的时候,一般必须要在使用前做monkey patch的原因
关于goroutine
goroutine的实现并不完全是传统意义上的协程。在协程阻塞的时候(cpu计算或者文件IO等),多个goroutine会变成多线程的方式执行。golang1.2之后还有类似erlang reduction的机制,来改善goroutine调度的公平性。这个机制只有在函数调用等场合下才会生效,所以效果还比较有限。
关于read/write hook
腾讯广州研究院的微信团队在一篇文章中透露出,他们使用hook read/write系统调用和swapcontext让出执行权的方式来实现类似于协程的效果,但是这个实现应该只在linux上可用,并且swapcontext还是要比协程切换更重一些。
评价列表(0)Java线程中断的本质深入理解
字体:[ ] 类型:转载 时间:
Java的中断是一种协作机制。也就是说调用线程对象的interrupt方法并不一定就中断了正在运行的线程,它只是要求线程自己在合适的时机中断自己,本文将详细介绍,需要了解的朋友可以参考下
一、Java中断的现象 首先,看看Thread类里的几个方法:
public static boolean&interrupted
测试当前线程是否已经中断。线程的中断状态&由该方法清除。换句话说,如果连续两次调用该方法,则第二次调用将返回 false(在第一次调用已清除了其中断状态之后,且第二次调用检验完中断状态前,当前线程再次中断的情况除外)。
public boolean&isInterrupted()
测试线程是否已经中断。线程的中断状态&不受该方法的影响。
public void&interrupt()
中断线程。上面列出了与中断有关的几个方法及其行为,可以看到interrupt是中断线程。如果不了解Java的中断机制,这样的一种解释极容易造成误解,认为调用了线程的interrupt方法就一定会中断线程。 其实,Java的中断是一种协作机制。也就是说调用线程对象的interrupt方法并不一定就中断了正在运行的线程,它只是要求线程自己在合适的时机中断自己。每个线程都有一个boolean的中断状态(不一定就是对象的属性,事实上,该状态也确实不是Thread的字段),interrupt方法仅仅只是将该状态置为true
代码如下: public class TestInterrupt { public static void main(String[] args) { Thread t = new MyThread(); t.start(); t.interrupt(); System.out.println("已调用线程的interrupt方法"); } static class MyThread extends Thread { public void run() { int num = longTimeRunningNonInterruptMethod(2, 0); System.out.println("长时间任务运行结束,num=" + num); System.out.println("线程的中断状态:" + Thread.interrupted()); } private static int longTimeRunningNonInterruptMethod(int count, int initNum) { for(int i=0; i& i++) { for(int j=0; j&Integer.MAX_VALUE; j++) { initNum ++; } } return initN } } }
一般情况下,会打印如下内容: 已调用线程的interrupt方法 长时间任务运行结束,num=-2 线程的中断状态:true 可见,interrupt方法并不一定能中断线程。但是,如果改成下面的程序,情况会怎样呢?
代码如下: import java.util.concurrent.TimeU public class TestInterrupt { public static void main(String[] args) { Thread t = new MyThread(); t.start(); t.interrupt(); System.out.println("已调用线程的interrupt方法"); } static class MyThread extends Thread { public void run() { int num = -1; try { num = longTimeRunningInterruptMethod(2, 0); } catch (InterruptedException e) { System.out.println("线程被中断"); throw new RuntimeException(e); } System.out.println("长时间任务运行结束,num=" + num); System.out.println("线程的中断状态:" + Thread.interrupted()); } private static int longTimeRunningInterruptMethod(int count, int initNum) throws InterruptedException{ for(int i=0; i& i++) { TimeUnit.SECONDS.sleep(5); } return initN } } }
经运行可以发现,程序抛出异常停止了,run方法里的后两条打印语句没有执行。那么,区别在哪里? 一般说来,如果一个方法声明抛出InterruptedException,表示该方法是可中断的(没有在方法中处理中断却也声明抛出InterruptedException的除外),也就是说可中断方法会对interrupt调用做出响应(例如sleep响应interrupt的操作包括清除中断状态,抛出InterruptedException),如果interrupt调用是在可中断方法之前调用,可中断方法一定会处理中断,像上面的例子,interrupt方法极可能在run未进入sleep的时候就调用了,但sleep检测到中断,就会处理该中断。如果在可中断方法正在执行中的时候调用interrupt,会怎么样呢?这就要看可中断方法处理中断的时机了,只要可中断方法能检测到中断状态为true,就应该处理中断。让我们为开头的那段代码加上中断处理。 那么自定义的可中断方法该如何处理中断呢?那就是在适合处理中断的地方检测线程中断状态并处理。
代码如下: public class TestInterrupt { public static void main(String[] args) throws Exception { Thread t = new MyThread(); t.start(); // TimeUnit.SECONDS.sleep(1);//如果不能看到处理过程中被中断的情形,可以启用这句再看看效果 t.interrupt(); System.out.println("已调用线程的interrupt方法"); } static class MyThread extends Thread { public void run() {
try { num = longTimeRunningNonInterruptMethod(2, 0); } catch (InterruptedException e) { throw new RuntimeException(e); } System.out.println("长时间任务运行结束,num=" + num); System.out.println("线程的中断状态:" + Thread.interrupted()); } private static int longTimeRunningNonInterruptMethod(int count, int initNum) throws InterruptedException { if(interrupted()) { throw new InterruptedException("正式处理前线程已经被请求中断"); } for(int i=0; i& i++) { for(int j=0; j&Integer.MAX_VALUE; j++) { initNum ++; } //假如这就是一个合适的地方 if(interrupted()) { //回滚数据,清理操作等 throw new InterruptedException("线程正在处理过程中被中断"); } } return initN } } }
如上面的代码,方法longTimeRunningMethod此时已是一个可中断的方法了。在进入方法的时候判断是否被请求中断,如果是,就不进行相应的处理了;处理过程中,可能也有合适的地方处理中断,例如上面最内层循环结束后。 这段代码中检测中断用了Thread的静态方法interrupted,它将中断状态置为false,并将之前的状态返回,而isInterrupted只是检测中断,并不改变中断状态。一般来说,处理过了中断请求,应该将其状态置为false。但具体还要看实际情形。 二、Java中断的本质 在历史上,Java试图提供过抢占式限制中断,但问题多多,例如已被废弃的Thread.stop、Thread.suspend和 Thread.resume等。另一方面,出于Java应用代码的健壮性的考虑,降低了编程门槛,减少不清楚底层机制的程序员无意破坏系统的概率。 如今,Java的线程调度不提供抢占式中断,而采用协作式的中断。其实,协作式的中断,原理很简单,就是轮询某个表示中断的标记,我们在任何普通代码的中都可以实现。 例如下面的代码:
代码如下: volatile bool isI //… while(!isInterrupted) { compute(); }
但是,上述的代码问题也很明显。当compute执行时间比较长时,中断无法及时被响应。另一方面,利用轮询检查标志变量的方式,想要中断wait和sleep等线程阻塞操作也束手无策。 如果仍然利用上面的思路,要想让中断及时被响应,必须在虚拟机底层进行线程调度的对标记变量进行检查。是的,JVM中确实是这样做的。下面摘自java.lang.Thread的源代码:
代码如下: public static boolean interrupted() { return currentThread().isInterrupted(true); } //… private native boolean isInterrupted(boolean ClearInterrupted);
可以发现,isInterrupted被声明为native方法,取决于JVM底层的实现。 实际上,JVM内部确实为每个线程维护了一个中断标记。但应用程序不能直接访问这个中断变量,必须通过下面几个方法进行操作:
代码如下: public class Thread { //设置中断标记 public void interrupt() { ... } //获取中断标记的值 public boolean isInterrupted() { ... } //清除中断标记,并返回上一次中断标记的值 public static boolean interrupted() { ... } ... }
通常情况下,调用线程的interrupt方法,并不能立即引发中断,只是设置了JVM内部的中断标记。因此,通过检查中断标记,应用程序可以做一些特殊操作,也可以完全忽略中断。你可能想,如果JVM只提供了这种简陋的中断机制,那和应用程序自己定义中断变量并轮询的方法相比,基本也没有什么优势。JVM内部中断变量的主要优势,就是对于某些情况,提供了模拟自动“中断陷入”的机制。 在执行涉及线程调度的阻塞调用时(例如wait、sleep和join),如果发生中断,被阻塞线程会“尽可能快的”抛出InterruptedException。因此,我们就可以用下面的代码框架来处理线程阻塞中断:
代码如下: try { //wait、sleep或join } catch(InterruptedException e) { //某些中断处理工作 }
所谓“尽可能快”,我猜测JVM就是在线程调度调度的间隙检查中断变量,速度取决于JVM的实现和硬件的性能。 三、一些不会抛出 InterruptedException 的线程阻塞操作 然而,对于某些线程阻塞操作,JVM并不会自动抛出InterruptedException异常。例如,某些I/O操作和内部锁操作。对于这类操作,可以用其他方式模拟中断: 1)java.io中的异步socket I/O 读写socket的时候,InputStream和OutputStream的read和write方法会阻塞等待,但不会响应java中断。不过,调用Socket的close方法后,被阻塞线程会抛出SocketException异常。 2)利用Selector实现的异步I/O 如果线程被阻塞于Selector.select(在java.nio.channels中),调用wakeup方法会引起ClosedSelectorException异常。 3)锁获取 如果线程在等待获取一个内部锁,我们将无法中断它。但是,利用Lock类的lockInterruptibly方法,我们可以在等待锁的同时,提供中断能力。 四、两条编程原则 另外,在任务与线程分离的框架中,任务通常并不知道自身会被哪个线程调用,也就不知道调用线程处理中断的策略。所以,在任务设置了线程中断标记后,并不能确保任务会被取消。因此,有以下两条编程原则: 1)除非你知道线程的中断策略,否则不应该中断它。 这条原则告诉我们,不应该直接调用Executer之类框架中线程的interrupt方法,应该利用诸如Future.cancel的方法来取消任务。2)任务代码不该猜测中断对执行线程的含义。 这条原则告诉我们,一般代码遇在到InterruptedException异常时,不应该将其捕获后“吞掉”,而应该继续向上层代码抛出。 总之,Java中的非抢占式中断机制,要求我们必须改变传统的抢占式中断思路,在理解其本质的基础上,采用相应的原则和模式来编程。
您可能感兴趣的文章:
大家感兴趣的内容
12345678910
最近更新的内容
常用在线小工具188被浏览9,046分享邀请回答2添加评论分享收藏感谢收起0添加评论分享收藏感谢收起java并发编程艺术总结_图文_百度文库
两大类热门资源免费畅读
续费一年阅读会员,立省24元!
java并发编程艺术总结
阅读已结束,下载文档到电脑
想免费下载本文?
定制HR最喜欢的简历
下载文档到电脑,方便使用
还剩14页未读,继续阅读
定制HR最喜欢的简历
你可能喜欢}

我要回帖

更多关于 线程与协程 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信