作者:hoojo
出处:
blog: http://blog.csdn.net/IBM_hoojo
1、线程概述
几乎所有的操作系统都支持同时运行多个任务,一个任务通常就是一个程序,每个运行中的程序就是一个进程。
当一个程序运行时,内部可能包含了多个顺序执行流,每个顺序执行流就是一个线程。
2、线程和进程
几乎所有的操作系统都有进程的概念,所有运行中的任务通常对应一条进程。当一个程序进入内存运行,就是一个进程了。
进程是处于运行中的程序,具有一定的独立能力,进程是系统进行资源分配和调度的一个独立单位。
进程特征:
A、独立性:进程是系统中独立存在的实体,可以拥有自己独立的资源,每个进程都拥有自己的私有地址地址。
在没有经过进程本身允许的情况下,一个用户进程不可以访问其他进程地址空间。
B、动态性:进程和程序的区别在于,程序只是一个静态的指令集合,而进程是一个正在系统中活动的指令集合。
在程序中加入了时间概念,进程具有自己的生命周期和各种不同的状态,这些概念是程序不具备的。
C、并发性:多个进程可以在单个处理器上并发执行,多个进程之间不会互相影响。
多线程则扩展了多进程的概念,使得同一个进程可以同时并发处理多个任务。线程也被称为轻量级进程(Lightweight Process),线程是进程的执行单元。
就像进程在操作系统中的地位一样,线程在程序中是独立、并发执行流。当进程被初始化后,主线程就被创建。对于绝大多数应用程序来说,通常仅要一个主线程,
但我们也可以在该进程内创建多条顺序执行流,这些顺序执行流就是线程,每条线程也互相独立的。
线程是进程的组成部分,一个进程可以拥有多个线程,一个线程必须有一个父进程。线程可以拥有自己的堆、栈、程序计数器、局部变量,但不能拥有系统资源,
它与父进程的其他线程共享该进程所有的全部资源。因为多个线程共享父进程的全部资源。
线程可以完成一定的任务,可与其他线程共享父进程中的变量和部分环境,相互之间协作共同完成进程所要完成的任务。
线程是独立运行的,它并不知道进程中是否还有其他进程存在。线程的执行是抢占方式的,也就是说,当前运行的线程在任何时候都可以被挂起,以便其他线程运行。
一个线程可以创建和撤销另一个线程,同一个进程中的多个线程可以并发执行。
综述:一个程序运行后至少有一个进程,一个进程可以包含多个线程。至少包含一个线程。
3、并发和并行
并发性(concurrency)和并行性(parallel)是两个概念;
并行指在同一时刻,有多条指令(线程)在多个处理器上同时执行;
并发指在同一时刻只能有一个指令(线程)执行,但多个进程指令被快速轮换执行,使得宏观上具有多个进程同时执行的效果。
4、多线程的优势
线程划分尺度小于进程,使得多线程划分的并发性高。进程在执行时有自己独立的单元,多个线程共享内存,从而提高了运行效率。
线程比进程具有更高的性能,这是由于同一个进程中的线程都有共性:多个线程将共享同一个进程的虚拟空间。
线程共性的环境包括:进程代码段、进程共有数据等。线程很容易就利用共性的数据进行通信。
当操作系统创建一个进程时,必须给该进程分别独立的内存空间,并分配大量相关的资源;但创建一个线程则简单得多,因此多线程来实现并发要比多进程实现并发的性能高得多。
多线程优点:
A、进程之间不能共享内存,但线程之间共享内存非常容易。
B、系统创建进程需要为该进程重新分配系统资源,但创建线程则代价要小得多,因此使用线程来实现多任务并发比多进程的效率高。
C、Java语言内置多线程功能支持,而不是单纯的作为底层操作系统的调度方式,从而简化Java的多线程编程。
5、线程的创建和启动A、继承Thread类或实现Runnable接口,重写或实现run方法,run方法代表线程要完成的任务B、创建Thread子类或是Runnable的实现类,即创建的线程对象;不同的是接口实现线程,需要将接口的实现类作为参数传递给Thread类的构造参数C、用线程对象的start方法启动线程6、继承Thread和实现Runnable接口创建线程的区别采用Runnable接口实现线程:优势:A、线程类只是实现了Runnable接口,还可以继承其他的类B、在这种方式下,可以多个线程共享同一个目标对象,所以很合适多个线程来处理同一份资源的情况,从而可以将CPU、代码和数据分开,形成清晰的模型,较好的面相对象思想。劣势:编程稍微复杂,如果需要访问当前线程需要用Thread.currentThread方法来获取采用继承Thread类的方式实现线程:优势:编写简单,如果要获得当前线程直接this即可劣势:线程类继承了Thread,不能在继承其他类相对而言,用Runnable的方式更好,具体可以根据当前需要而定;7、线程生命周期线程被创建启动后,不并不是启动后就进入了执行状态,也不是一直处于的执行状态。线程的生命周期分为创建(new)、就绪(Runnable)、运行(running)、阻塞(Blocked)、死亡(Dead)五种状态。
线程启动后不会一直霸占CPU资源,所以CPU需要在多条线程中切换执行,线程就会在多次的运行和阻塞中切换。8、新建(new)和就绪(Runnable)状态
当new一个线程后,该线程处于新建状态,此时它和Java对象一样,仅仅由Java虚拟机为其分配内存空间,并初始化成员变量。此时线程对象没有表现出任何的动态特征,程序也不会执行线程的执行体。注意:run方法是线程的执行体,不能由我们手动调用。我们可以用start方法启动线程,系统会把run方法当成线程的执行体来运行,如果直接调用线程对象run方法,则run方法立即会被运行。而且在run方法返回之前其他线程无法并行执行,也就是说系统会把当前线程类当成一个普通的Java对象,而run方法也是一个普通的方法,而不是线程的执行体。9、运行(running)和阻塞(Blocked)状态如果处于就绪状态的线程就获得了CPU,开始执行run方法的线程执行体,则该线程处于运行状态。单CPU的机器,任何时刻只有一条线程处于运行状态。当然,在多CPU机器上将会有多线程并行(parallel)执行,当线程大于CPU数量时,依然会在同一个CPU上切换执行。线程运行机制:一个线程运行后,它不可能一直处于运行状态(除非它执行的时间很短,瞬间执行完成),线程在运行过程中需要中断,目的是让其他的线程有运行机会,线程的调度取决于底层的策略。对应抢占式的系统而言,系统会给每个可执行的线程一个小时间段来处理任务,当时间段到达系统就会剥夺该线程的资源,让其他的线程有运行的机会。在选择下一个线程时,系统会考虑线程优先级。以下情况会出现线程阻塞状态:A、线程调用sleep方法,主动放弃占用的处理器资源B、线程调用了阻塞式IO方法,在该方法返回前,该线程被阻塞C、线程试图获得一个同步监视器,但该同步监视器正被其他线程所持有。D、线程等待某个通知(notify)E、程序调用了suspend方法将该线程挂起。不过这个方法容易导致死锁,尽量不免使用该方法当线程被阻塞后,其他线程将有机会执行。被阻塞的线程会在合适的时候重新进入就绪状态,注意是就绪状态不是运行状态。也就是被阻塞线程在阻塞解除后,必须重新等待线程调度器再次调用它。针对上面线程阻塞的情况,发生以下特定的情况可以解除阻塞,让进程进入就绪状态:A、调用sleep方法的经过了指定的休眠时间B、线程调用的阻塞IO已经返回,阻塞方法执行完毕C、线程成功获得了试图同步的监视器D、线程正在等待某个通知,其他线程发出了通知E、处于挂起状态的线程调用了resume恢复方法线程从阻塞状态只能进入就绪状态,无法进入运行状态。而就绪和运行状态之间的转换通常不受程序控制,而是由系统调度所致的。当就绪状态的线程获得资源时,该线程进入运行状态;当运行状态的线程事情处理器资源时就进入了就绪状态。但对调用了yield的方法就例外,此方法可以让运行状态转入就绪状态。10、线程死亡(Dead)状态线程会在以下方式进入死亡状态:A、run方法执行完成,线程正常结束B、线程抛出未捕获的异常或ErrorC、直接调用该线程的stop方法来结束线程—该方法易导致死锁,注意使用注意:当主线程结束的时候,其他线程不受任何影响。一旦子线程启动后,会拥有和主线程相同的地位,不受主线程影响。isAlive方法可以测试当前线程是否死亡,当线程处于就绪、运行、阻塞状态,该方法返回true,如果线程处于新建或死亡状态就会返回false。不要试图对死亡的线程调用start方法,来启动它。死亡线程不可能再次运行。11、控制线程Java线程提供了很多工具方法,这些方法都很好的控制线程A、join线程让一个线程等待另一个线程完成的方法。当某个程序执行流中调用其他线程的join方法时,调用线程将会被阻塞,直到被join方法的join线程执行完成为止。join方法通常有使用线程的程序调用,将大问题划分成许多小问题。每个小问题分配一个线程。当所有的小问题得到处理后,再调用主线程进一步操作。join有三种重载模式:一、join等待被join的线程执行完成二、join(long millis)等待被join的线程时间最长为millis毫秒,如果在millis毫秒外,被join的线程还没有执行完则不再等待
三、join(long millis, int nanos)被join的线程等待时间长为millis毫秒加上nanos微秒通常我们很少用第三种join,原因有二:程序对时间的精度无需精确到千分之一毫秒计算机硬件、操作系统也无法做到精确到千分之一毫秒B、后台线程有一种线程,在后台运行,它的任务是为其他线程提供服务,这种线程被称为“后台线程(Daemon Thread)”,有被称为“守护线程”或“精灵线程”。JVM的垃圾回收器线程就是后台进程。后台进程有个特征是:如果前台的进程都死亡,那么后台进程也死亡。(它为前台进程服务)用Thread的setDaemon (true)方法可以指定当前线程为后台线程。注意:前台线程执行完成死亡后,JVM会通知后台线程,后台线程就会死亡。但它得到通知到后台线程作成响应,需要一段时间,而且要将某个线程设置为后台线程,必需要在该线程启动前设置,也就是说设置setDaemon必需在start方法前面调用。否则会出现java.lang.IllegalThreadStateException异常C、线程休眠sleep如果需要当前线程暂停一段时间,并进入阻塞状态就需要用sleep,sleep有2中重载方式:sleep(long millis)让当前线程暂停millis毫秒后,并进入阻塞状态,该方法受系统计时器和线程调度器的影响
sleep(long millis, int nanos)让当前正在执行的线程暂停millis毫秒+nanos微秒,并进入阻塞当调用sleep方法进入阻塞状态后,在sleep时间段内,该线程不会获得执行机会,即使没有其他可运行的线程,处于sleep的线程不会执行。D、线程让步yieldyield和sleep有点类似,它也可以让当前执行的线程暂停,但它不会阻塞线程,只是将该线程转入到就绪状态。yield只是让当前线程暂停下,让系统线程调度器重新调度下。当yield的线程后,当前线程暂停。系统线程调度器会让优先级相同或是更高的线程运行。sleep和yield的区别(1)、sleep方法暂停当前线程后,会给其他线程执行集合,不会理会线程的优先级。但yield则会给优先级相同或高优先级的线程执行机会(2)、sleep方法会将线程转入阻塞状态,直到经过阻塞时间才会转入到就绪状态;而yield则不会将线程转入到阻塞状态,它只是强制当前线程进入就绪状态。因此完全有可能调用yield方法暂停之后,立即再次获得处理器资源继续运行。(3)、sleep声明抛出了InterruptedException异常,所以调用sleep方法时,要么捕获异常,要么抛出异常。而yield没有申明抛出任何异常E、改变线程优先级每个线程都有优先级,优先级决定线程的运行机会的多少。每个线程默认和它创建的父类的优先级相同,main方法的优先级是普通优先级,那在main方法中创建的子线程都是普通优先级。getPriority(int newPriority)/setPriority(int)设置优先级有以下级别:MAX_PRIORITY 值是10MIN_PRIORITY 值是1NORM_PRIORITY 值是5范围是1-10;12、线程同步当多个线程访问同一个数据时,非常容易出现线程安全问题。这时候就需要用线程同步Case:银行取钱问题,有以下步骤:A、用户输入账户、密码,系统判断是否登录成功B、用户输入取款金额C、系统判断取款金额是否大于现有金额D、如果金额大于取款金额,就成功,否则提示小于余额现在模拟2个人同时对一个账户取款,多线程操作就会出现问题。这时候需要同步才行;同步代码块:synchronized (object) {
//同步代码
}Java多线程支持方法同步,方法同步只需用用synchronized来修饰方法即可,那么这个方法就是同步方法了。对于同步方法而言,无需显示指定同步监视器,同步方法监视器就是本身this同步方法:public synchronized void editByThread() {//doSomething
}需要用同步方法的类具有以下特征:A、该类的对象可以被多个线程访问B、每个线程调用对象的任意都可以正常的结束,返回正常结果C、每个线程调用对象的任意方法后,该对象状态保持合理状态不可变类总是线程安全的,因为它的对象状态是不可改变的,但可变类对象需要额外的方法来保证线程安全。例如Account就是一个可变类,它的money就是可变的,当2个线程同时修改money时,程序就会出现异常或错误。所以要对Account设置为线程安全的,那么就需要用到同步synchronized关键字。下面的方法用synchronized同步关键字修饰,那么这个方法就是一个同步的方法。这样就只能有一个线程可以访问这个方法,在当前线程调用这个方法时,此方法是被锁状态,同步监视器是this。只有当此方法修改完毕后其他线程才能调用此方法。这样就可以保证线程的安全,处理多线程并发取钱的的安全问题。public synchronized void drawMoney(double money) {//取钱操作
}注意:synchronized可以修饰方法、代码块,但不能修饰属性、构造方法可变类的线程安全是以降低程序的运行效率为代价,为了减少线程安全所带来的负面影响,可以采用以下策略:A、不要对线程安全类的所有方法都采用同步模式,只对那些会改变竞争资源(共享资源)的方法进行同步。B、如果可变类有2中运行环境:单线程环境和多线程环境,则应该为该可变提供2种版本;线程安全的和非线程安全的版本。在单线程下采用非线程安全的提高运行效率保证性能,在多线程环境下采用线程安全的控制安全性问题。释放同步监视器的锁定任何线程进入同步代码块、同步方法之前,必须先获得对同步监视器的锁定,那么何时会释放对同步监视器锁定?程序无法显示的释放对同步监视器的锁定,线程可以通过以下方式释放锁定:A、当线程的同步方法、同步代码库执行结束,就可以释放同步监视器B、当线程在同步代码库、方法中遇到break、return终止代码的运行,也可释放C、当线程在同步代码库、同步方法中遇到未处理的Error、Exception,导致该代码结束也可释放同步监视器D、当线程在同步代码库、同步方法中,程序执行了同步监视器对象的wait方法,导致方法暂停,释放同步监视器下面情况不会释放同步监视器:A、当线程在执行同步代码库、同步方法时,程序调用了Thread.sleep()/Thread.yield()方法来暂停当前程序,当前程序不会释放同步监视器B、当线程在执行同步代码库、同步方法时,其他线程调用了该线程的suspend方法将该线程挂起,该线程不会释放同步监视器。注意尽量避免使用suspend、resume同步锁(Lock)通常认为:Lock提供了比synchronized方法和synchronized代码块更广泛的锁定操作,Lock更灵活的结构,有很大的差别,并且可以支持多个Condition对象Lock是控制多个线程对共享资源进行访问的工具。通常,锁提供了对共享资源的独占访问,每次只能有一个线程对Lock对象加锁,线程开始访问共享资源之前应先获得Lock对象。不过某些锁支持共享资源的并发访问,如:ReadWriteLock(读写锁),在线程安全控制中,通常使用ReentrantLock(可重入锁)。使用该Lock对象可以显示加锁、释放锁。class C {
//锁对象
private final ReentrantLock lock = new ReentrantLock();......//保证线程安全方法
public void method() {//上锁
lock.lock();try {
//保证线程安全操作代码
} catch() {
} finally {
lock.unlock();//释放锁
}}}使用Lock对象进行同步时,锁定和释放锁时注意把释放锁放在finally中保证一定能够执行。使用锁和使用同步很类似,只是使用Lock时显示的调用lock方法来同步。而使用同步方法synchronized时系统会隐式使用当前对象作为同步监视器,同样都是“加锁->访问->释放锁”的操作模式,都可以保证只能有一个线程操作资源。同步方法和同步代码块使用与竞争资源相关的、隐式的同步监视器,并且强制要求加锁和释放锁要出现在一个块结构中,而且获得多个锁时,它们必须以相反的顺序释放,且必须在与所有锁被获取时相同的范围内释放所有资源。Lock提供了同步方法和同步代码库没有的其他功能,包括用于非块结构的tryLock方法,已经试图获取可中断锁lockInterruptibly()方法,还有获取超时失效锁的tryLock(long, timeUnit)方法。
ReentrantLock具有重入性,也就是说线程可以对它已经加锁的ReentrantLock再次加锁,ReentrantLock对象会维持一个计数器来追踪lock方法的嵌套调用,线程在每次调用lock()加锁后,必须显示的调用unlock()来释放锁,所以一段被保护的代码可以调用另一个被相同锁保护的方法。死锁当2个线程相互等待对方是否同步监视器时就会发生死锁,JVM没有采取处理死锁的措施,这需要我们自己处理或避免死锁。一旦死锁,整个程序既不会出现异常,也不会出现错误和提示,只是线程将处于阻塞状态,无法继续。主线程保持对Foo的锁定,等待对Bar对象加锁,而副线程却对Bar对象保持锁定,等待对Foo加锁2条线程相互等待对方先释放锁,进入死锁状态。由于Thread类的suspend也很容易导致死锁,所以Java不推荐使用此方法暂停线程。13、线程通信(1)、线程的协调运行场景:用2个线程,这2个线程分别代表存款和取款。——现在系统要求存款者和取款者不断重复的存款和取款的动作,而且每当存款者将钱存入账户后,取款者立即取出这笔钱。不允许2次连续存款、2次连续取款。实现上述场景需要用到Object类,提供的wait、notify和notifyAll三个方法,这3个方法并不属于Thread类。但这3个方法必须由同步监视器调用,可分为2种情况:A、对于使用synchronized修饰的同步方法,因为该类的默认实例this就是同步监视器,所以可以在同步中直接调用这3个方法。B、对于使用synchronized修改的同步代码块,同步监视器是synchronized后可括号中的对象,所以必须使用括号中的对象调用这3个方法方法概述:一、wait方法:导致当前线程进入等待,直到其他线程调用该同步监视器的notify方法或notifyAll方法来唤醒该线程。wait方法有3中形式:无参数的wait方法,会一直等待,直到其他线程通知;带毫秒参数的wait和微妙参数的wait,这2种形式都是等待时间到达后苏醒。调用wait方法的当前线程会释放对该对象同步监视器的锁定。二、notify:唤醒在此同步监视器上等待的单个线程。如果所有线程都在此同步监视器上等待,则会随机选择唤醒其中一个线程。只有当前线程放弃对该同步监视器的锁定后(用wait方法),才可以执行被唤醒的线程。三、notifyAll:唤醒在此同步监视器上等待的所有线程。只有当前线程放弃对该同步监视器的锁定后,才能执行唤醒的线程。(2)、条件变量控制协调如果程序不使用synchronized关键字来保证同步,而是直接使用Lock对象来保证同步,则系统中不存在隐式的同步监视器对象,也不能使用wait、notify、notifyAll方法来协调进程的运行。当使用Lock对象同步,Java提供一个Condition类来保持协调,使用Condition可以让那些已经得到Lock对象却无法组合使用,为每个对象提供了多个等待集(wait-set),这种情况下,Lock替代了同步方法和同步代码块,Condition替代同步监视器的功能。Condition实例实质上被绑定在一个Lock对象上,要获得特定的Lock实例的Condition实例,调用Lock对象的newCondition即可。Condition类方法介绍:一、await:类似于隐式同步监视器上的wait方法,导致当前程序等待,直到其他线程调用Condition的signal方法和signalAll方法来唤醒该线程。该await方法有跟多获取变体:long awaitNanos(long nanosTimeout),void awaitUninterruptibly()、awaitUntil(Date daadline)二、signal:唤醒在此Lock对象上等待的单个线程,如果所有的线程都在该Lock对象上等待,则会选择随机唤醒其中一个线程。只有当前线程放弃对该Lock对象的锁定后,使用await方法,才可以唤醒在执行的线程。三、signalAll:唤醒在此Lock对象上等待的所有线程。只有当前线程放弃对该Lock对象的锁定后,才可以执行被唤醒的线程。(3)、使用管道流线程通信使用管道流,管道流有3种形式:PipedInputStream、PipedOutputStream、PipedReader和PipedWriter以及Pipe.SinkChannel和Pipe.SourceChannel,它们分别是管道流的字节流、管道字符流和新IO的管道Channel。管道流通信基本步骤:A、使用new操作法来创建管道输入、输出流B、使用管道输入流、输出流的connect方法把2个输入、输出流连接起来C、将管道输入、输出流分别传入2个线程D、2个线程可以分别依赖各自的管道输入流、管道输出流进行通信14、线程组和未处理异常ThreadGroup表示线程组,它可以表示一批线程进行分类管理,Java允许程序对Java允许直接对线程组控制,对线程组控制相对于同时控制这批线程。用户创建的所有线程都属于指定的线程组。如果程序没有值得线程属于哪个组,那这个线程就属于默认线程组。在默认情况下,子线程和创建它父线程属于同一组。一旦某个线程加入了指定线程组之后,该线程将属于该线程组,直到该线程死亡,线程运行中途不能改变它所属的线程组。Thread类提供一些构造设置线程所属的哪个组,具有以下方法:A、Thread(ThreadGroup group, Runnable target):target的run方法作为线程执行体创建新线程,属于group线程组B、Thread(ThreadGroup group, Runnalbe target, String name):target的run方法作为线程执行体创建的新线程,该线程属于group线程组,且线程名为nameC、Thread(ThreadGroup group, String name):创建新线程,新线程名为name,属于group组因为中途不能改变线程所属的组,所以Thread提供ThreadGroup的setter方法,但提供了getThreadGroup方法来返回该线程所属的线程组,getThreadGroup方法的返回值是ThreadGroup对象的表示,表示一个线程组。ThreadGroup有2个构造形式:A、ThreadGroup(String name):name线程组的名称B、ThreadGroup(ThreadGroup parent, String name):指定名称、指定父线程组创建的一个新线程组上面的构造都指定线程名称,也就是线程组都必须有自己的一个名称,可以通过调用ThreadGroup的getName方法得到,但不允许中途改变名称。ThreadGroup有以下常用的方法:A、activeCount:返回线程组活动线程数目B、interrupt:中断此线程组中的所有线程C、isDeamon:判断该线程是否在后台运行D、setDeamon:把该线程组设置为后台线程组,后台线程具有一个特征,当后台线程的最后一个线程执行结束或最后一个线程被销毁,后台线程组自动销毁。E、setMaxPriority:设置线程组最高优先级uncaughtException(Thread t, Throwable e)该方法可以处理该线程组内的线程所抛出的未处理的异常,Thread.UncaughtExceptionHandler是Thread类的一个内部公共静态接口,该接口内只有一个方法:void uncaughtException(Thread t, Throwable e) 该方法中的t代表出现异常的线程,而e代表该线程抛出的异常
Thread类中提供2个方法来设置异常处理器:A、staticsetDefaultUnaughtExceptionHandler(Thread.UncaughtExceptionHandler eh):为该线程类的所有线程实例设置默认的异常处理器B、setUncaughtExceptionHandler(Thread.UncaughtExceptionHander eh):为指导线程实例设置异常处理器ThreadGroup实现了Thread.UncaughtExceptionHandler接口,所以每个线程所属的线程组将会作为默认的异常处理器。当一个线程抛出未处理异常时,JVM会首先查找该异常对应的异常处理器,(setUncaughtExceptionHandler设置异常处理器),如果找到该异常处理器,将调用该异常处理器处理异常。否则,JVM将会调用该线程的所属线程组的uncaughtException处理异常,线程组处理异常流程如下:A、如果该线程有父线程组,则调用父线程组的uncaughtException方法来处理异常B、如果该线程实例所属的线程类有默认的异常处理器(setDefaultUnaughtExceptionHandler方法设置异常处理器),那就调用该异常处理器来处理异常信息C、将异常调用栈的信息打印到System.err错误输出流,并结束该线程15、Callable和FutureCallable接口定义了一个call方法可以作为线程的执行体,但call方法比run方法更强大:A、call方法可以有返回值B、call方法可以申明抛出异常Callable接口是JDK5后新增的接口,而且不是Runnable的子接口,所以Callable对象不能直接作为Thread的target。而且call方法还有一个返回值,call方法不能直接调用,它作为线程的执行体被调用。那么如何接收call方法的返回值?JDK1.5提供了Future接口来代表Callable接口里的call方法的返回值,并为Future接口提供了一个FutureTask实现类,该实现类实现Future接口,并实现了Runnable接口—可以作为Thread的target。Future接口里定义了如下几个公共方法控制他关联的Callable任务:A、boolean cancel(Boolean mayInterruptlfRunning):试图取消该Future里关联的Callable任务
B、V get():返回Callable任务里的call方法的返回值,调用该方法将导致线程阻塞,必须等到子线程结束才得到返回值C、V get(long timeout, TimeUnit unit):返回Callable任务里的call方法的返回值,该方法让程序最多阻塞timeout和unit指定的时间。
如果经过指定时间后Callable任务依然没有返回值,将会抛出TimeoutException。D、boolean isCancelled:如果在Callable任务正常完成前被取消,则返回true。
E、boolean isDone:如果Callable任务已经完成,则返回true
创建、并启动有返回值的线程的步骤如下:一、创建Callable接口的实现类,并实现call方法,该call方法的返回值,并作为线程的执行体。二、创建Callable实现类的实例,使用FutureTask类来包装Callable对象,该FutureTask对象封装了该Callable对象的call方法的返回值三、使用FutureTask对象作为Thread对象的target创建、并启动新线程四、调用FutureTask对象的方法来获得子线程执行结束后的返回值作者:hoojo
http://www.cnblogs.com/hoojo/archive/2011/05/05/2038101.html
出处:
blog: http://blog.csdn.net/IBM_hoojo