Runable Thread

来源:互联网 发布:使命召唤fa7.62l数据 编辑:程序博客网 时间:2024/04/29 21:59

中断线程

线程结束的方法:

Run方法执行完毕
出现在线程中没有捕获的异常

强制线程终止的方法:

voidinterrupt()
Interrupts this thread.
该方法将线程的中断标志置为True。

如果线程被阻塞,就无法检查到中断状态,这是产生InterruptException异常的地方。例如

  • 一个被阻塞的线程(sleep或者wait)上调用Interrupt方法,阻塞调用(即interrupt方法)将被InterruptedException异常中断。
  • 如果在中断状态被置为TRUE时调用sleep方法,它不会休眠,相反,它将清除中断状态位,并抛出InterruptedException。 ------------------ 所以在Sleep时,要么捕获异常,要么抛出
  • interrupted方法是静态方法,将中断位置置为TRUE;isInterrupted方法是实例方法,可用来检验是否有线程被中断,不改变中断位状态。


如果一个线程由于等待某些事件的发生而被阻塞,又该如何实现该线程的中断呢?比如当一个线程由于需要等候键盘输入而被阻塞,处于不可运行状态时,即使主程序中将该线程的共享变量设置为true,但该线程此时根本无法检查循环标志,当然也就无法立即中断。
其实,这种情况经常会发生,比如调用Thread.join()方法,或者Thread.sleep()方法,在网络中调用 ServerSocket.accept()方法,或者调用了DatagramSocket.receive()方法时,都有可能导致线程阻塞。即便这 样,仍然不要使用stop()方法,而是使用Thread提供的interrupt()方法,因为该方法虽然不会中断一个正在运行的线程,但是它可以使一 个被阻塞的线程抛出一个中断异常,从而使线程提前结束阻塞状态,退出堵塞代码。

Interrupted机制实现是用了中间的标志位interrupt status。调用Thread.interrupted这个静态方法会检查状态并清空重置状态,而非静态方法isInterrupted只是进行状态的查询,无清空重置。即:

Thread.interrupted 是 currentThread.isInterrupted(true) , 会返回当前的interrupted状态位,并清空状态
thread.isInterrupted 是 isInterrupted(false) 只返回状态,不做清空处理

interrupt 方法并不直接中断线程或者抛出InterruptedException,而是设置 interrupted 标志位。Object.wait, Thread.sleep方法,会不断的轮询监听 interrupted 标志位,发现其设置为true后,会停止阻塞并抛出 InterruptedException异常。
Object.wait, Thread.join, Thread.sleep,  interrupt调用后,会清空其interrupt状态,并抛出InterruptedException异常

注意:这里有三个方法: interrupt interrupted isinterrupted

线程状态类型

1. 新建状态(New):新创建了一个线程对象。
2. 就绪状态(Runnable):线程对象创建后,其他线程调用了该对象的start()方法。该状态的线程位于可运行线程池中,变得可运行,等待获取CPU的使用权。

3. 运行状态(Running):就绪状态的线程获取了CPU,执行程序代码。

4. 阻塞状态(Blocked):阻塞状态是线程因为某种原因放弃CPU使用权,暂时停止运行。直到线程进入就绪状态,才有机会转到运行状态。阻塞的情况分三种:
(一)、等待阻塞:运行的线程执行wait()方法,JVM会把该线程放入等待池中。
(二)、同步阻塞:运行的线程在获取对象的同步锁时,若该同步锁被别的线程占用,则JVM会把该线程放入锁池中。
(三)、其他阻塞:运行的线程执行sleep()或join()方法,或者发出了I/O请求时,JVM会把该线程置为阻塞状态。当sleep()状态超时、join()等待线程终止或者超时、或者I/O处理完毕时,线程重新转入就绪状态。
5. 死亡状态(Dead):线程执行完了或者因异常退出了run()方法,该线程结束生命周期。

 线程状态图


Java线程阻塞的区别:

阻塞指的是暂停一个线程的执行以等待某个条件发生(如某资源就绪)。Java 提供了大量方法来支持阻塞,下面让对它们逐一分析。

1. sleep()方法:sleep()允许指定以毫秒为单位的一段时间作为参数,它使得线程在指定的时间内进入阻塞状态,不能得到CPU 时间,指定的时间一过,线程重新进入可执行状态。

典型地,sleep() 被用在等待某个资源就绪的情形:测试发现条件不满足后,让线程阻塞一段时间后重新测试,直到条件满足为止。

2.(Java 5已经不推荐使用,易造成死锁!!) suspend()和resume()方法:两个方法配套使用,suspend()使得线程进入阻塞状态,并且不会自动恢复,必须其对应的 resume() 被调用,才能使得线程重新进入可执行状态。典型地,suspend() 和 resume() 被用在等待另一个线程产生的结果的情形:测试发现结果还没有产生后,让线程阻塞,另一个线程产生了结果后,调用resume()使其恢复。

3. yield() 方法:yield() 使得线程放弃当前分得的 CPU 时间,但是不使线程阻塞,即线程仍处于可执行状态,随时可能再次分得 CPU 时间。调用 yield() 的效果等价于调度程序认为该线程已执行了足够的时间从而转到另一个线程。

4. wait() 和 notify() 方法:两个方法配套使用,wait() 使得线程进入阻塞状态,它有两种形式,一种允许指定以毫秒为单位的一段时间作为参数,另一种没有参数,前者当对应的 notify() 被调用或者超出指定时间时线程重新进入可执行状态,后者则必须对应的 notify() 被调用。

了解锁机制:http://blog.csdn.net/yangzhijun_cau/article/details/6432216

  2和4区别的核心在于,前面叙述的所有方法,阻塞时都不会释放占用的锁(如果占用了的话),而这一对方法(wait、notify)则相反。上述的核心区别导致了一系列的细节上的区别。

  首先,前面叙述的所有方法都隶属于Thread 类,但是这一对却直接隶属于 Object 类,也就是说,所有对象都拥有这一对方法。因为这一对方法阻塞时要释放占用的锁,而锁是任何对象都具有的,调用任意对象的 wait() 方法导致线程阻塞,并且该对象上的锁被释放。而调用任意对象的notify()方法则导致因调用该对象的 wait() 方法而阻塞的线程中随机选择的一个解除阻塞(但要等到获得锁后才真正可执行)。

  其次,前面叙述的所有方法都可在任何位置调用,但是这一对方法却必须在 synchronized 方法或块中调用,理由也很简单,只有在synchronized 方法或块中当前线程才占有锁,才有锁可以释放。同样的道理,调用这一对方法的对象上的锁必须为当前线程所拥有,这样才有锁可以释放。因此,这一对方法调用必须放置在这样的 synchronized 方法或块中,该方法或块的上锁对象就是调用这一对方法的对象。若不满足这一条件,则程序虽然仍能编译,但在运行时会出现IllegalMonitorStateException异常。

  wait() 和 notify() 方法的上述特性决定了它们经常和synchronized 方法或块一起使用,将它们和操作系统的进程间通信机制作一个比较就会发现它们的相似性:synchronized方法或块提供了类似于操作系统原语的功能,它们的结合用于解决各种复杂的线程间通信问题。

关于 wait() 和 notify() 方法最后再说明两点:

  第一:调用 notify() 方法导致解除阻塞的线程是从因调用该对象的 wait() 方法而阻塞的线程中随机选取的,我们无法预料哪一个线程将会被选择,所以编程时要特别小心,避免因这种不确定性而产生问题。

  第二:除了 notify(),还有一个方法 notifyAll() 也可起到类似作用,唯一的区别在于,调用 notifyAll() 方法将把因调用该对象的wait()方法而阻塞的所有线程一次性全部解除阻塞。当然,只有获得锁的那一个线程才能进入可执行状态。


优先级

  • 默认情况下,一个线程继承它的父线程
  • 可利用setPriority来设置线程  MIN_PRIORITY =1 MAX_PRIORITY=10 NORMPRIORITY=5;
  • 线程的优先级是高度依赖于系统的(比如装在Linux上的Java就依赖于Linux系统),Java线程的优先级,将会映射到对应的Linux优先级上,所以,不要将程序的正确性依赖于优先级
  • yield() 方法为同级优先级让步。

守护线程

参考:http://blog.csdn.net/shimiso/article/details/8964414

1、什么是守护线程(Daemon线程)?

守护线程是一个后台运行的线程,与之对比的是用户线程(User线程)。它从属于创建它的线程,因此当创建守护的线程结束时,守护线程也随之消亡。当正在运行的线程都是守护线程时,Java 虚拟机退出。

2、如何创建守护线程?

创建一个新线程,用setDaemon(boolean on)方法可以方便的设置线程的Daemon模式,true为Daemon模式,false为User模式。setDaemon(boolean on)方法必须在线程启动之前调用,当线程正在运行时调用会产生异常。isDaemon方法将测试该线程是否为守护线程。值得一提的是,当你在一个守护线程中产生了其他线程,那么这些新产生的线程不用设置Daemon属性,都将是守护线程,用户线程同样。

3、守护线程的用处?

守护线程使用的情况较少,但并非无用,举例来说,JVM的垃圾回收、内存管理等线程都是守护线程。还有就是在做数据库应用时候,使用的数据库连接池,连接池本身也包含着很多后台线程,监控连接个数、超时时间、状态等等。
守护线程一般是为用户线程服务的。

4、守护线程与用户线程的区别.

用户线程拥有自己的生命周期,不依赖于创建它的线程。在创建它的线程结束后用户线程仍能继续运行。运行有限的一段时间的线程一般来说是用户纯种。
守护线程在创建的线程结束时消亡。无限运行的线程通常应该定义成守护线程,只是因为需要一种结束它们的方式。
注意:守护线程永远不可以访问固有资源,如文件,数据库,因为它可能在任何时刻被中断结束。

未捕获异常

在线程中,未被检测到的异常将导致线程终止,但是这种异常,不需要Catch捕获,因为在线程死亡之前,异常就被传递到了一个用于未捕获异常的处理器。
这个处理器要实现两点:
1 必须实现Thread.UncaughtExceptionHandler接口
2 调用setUncatghtExceptionHandler为线程安装一个处理器,或者调用静态的setDefaultUncatghtExceptionHandler为所有的线程安装一个默认的处理器。
3 如果没有为线程安装独立的处理器,此时线程的处理器就是该线程的ThreadGrounp对象:注意:ThreadGroup对象实现了Thread.UncaughtExceptionHandler接口    它的uncaughtExcepiton方法做了什么呢?见P630页


0 0
原创粉丝点击