疯狂Java学习笔记(64)------------线程转换
2015-05-31 18:30
274 查看
线程的生命周期
由上图可以看出:
线程的状态转换是线程控制的基础。线程状态总的可分为五大状态:分别是生、死、可运行、运行、等待/阻塞。
各个状态细节不再赘述,具体请看/article/1325091.html此篇博文
此篇博文主要讲述各个状态的实例!
一、新建和就绪状态
此处不做细致的讲解,在上一篇博文中已经讲解过了!
二、阻止线程执行
堵塞状态是前述四种状态中最有趣的,值得我们作进一步的探讨。线程被堵塞可能是由下述五方面的原因造成的:
(1) 调用sleep(毫秒数),使线程进入“睡眠”状态。在规定的时间内,这个线程是不会运行的。
(2) 用suspend()暂停了线程的执行。除非线程收到resume()消息,否则不会返回“可运行”状态。
(3) 用wait()暂停了线程的执行。除非线程收到nofify()或者notifyAll()消息,否则不会变成“可运行”(是的,这看起来同原因2非常相象,但有一个明显的区别是我们马上要揭示的)。
(4) 线程正在等候一些IO(输入输出)操作完成。
(5) 线程试图调用另一个对象的“同步”方法,但那个对象处于锁定状态,暂时无法使用。
解决上面的堵塞,让线程重新进入就绪状态:
1、调用sleep()方法的的线程经过的指定的时间。
2、线程调用的阻塞式IO方法返回、
3、线程成功的获得了试图取得的同步监视器。
4、线程正在等待某个通知,其他线程发出了一个通知。
5、处于挂起状态的线程被调用了resume()恢复方法。
注意:
1、线程睡眠是帮助所有线程获得运行机会的最好方法。
2、线程睡眠到期自动苏醒,并返回到可运行状态,不是运行状态。sleep()中指定的时间是线程不会运行的最短时间。因此,sleep()方法不能保证该线程睡眠到期后就开始执行。
3、sleep()是静态方法,只能控制当前正在运行的线程。
1、睡眠
Thread.sleep(long millis)和Thread.sleep(long millis, int nanos)静态方法强制当前正在执行的线程休眠(暂停执行),以“减慢线程”。当线程睡眠时,它入睡在某个地方,在苏醒之前不会返回到可运行状态。当睡眠时间到期,则返回到可运行状态。
线程睡眠的原因:线程执行太快,或者需要强制进入下一轮,因为Java规范不保证合理的轮换。
睡眠的实现:调用静态方法。
try {
Thread.sleep(123);
} catch (InterruptedException e) {
e.printStackTrace();
}
睡眠的位置:为了让其他线程有机会执行,可以将Thread.sleep()的调用放线程run()之内。这样才能保证该线程执行过程中会睡眠。
运行结果:
2、线程的优先级和线程让步yield()
线程的让步是通过Thread.yield()来实现的。yield()方法的作用是:暂停当前正在执行的线程对象,并执行其他线程。
要理解yield(),必须了解线程的优先级的概念。线程总是存在优先级,优先级范围在1~10之间。JVM线程调度程序是基于优先级的抢先调度机制。在大多数情况下,当前运行的线程优先级将大于或等于线程池中任何线程的优先级。但这仅仅是大多数情况。
注意:当设计多线程应用程序的时候,一定不要依赖于线程的优先级。因为线程调度优先级操作是没有保障的,只能把线程优先级作用作为一种提高程序效率的方法,但是要保证程序不依赖这种操作。
当线程池中线程都具有相同的优先级,调度程序的JVM实现自由选择它喜欢的线程。这时候调度程序的操作有两种可能:一是选择一个线程运行,直到它阻塞或者运行完成为止。二是时间分片,为池内的每个线程提供均等的运行机会。
设置线程的优先级:线程默认的优先级是创建它的执行线程的优先级。可以通过setPriority(int newPriority)更改线程的优先级。例如:
Thread t = new MyThread();
t.setPriority(8);
t.start();
线程优先级为1~10之间的正整数,JVM从不会改变一个线程的优先级。然而,1~10之间的值是没有保证的。一些JVM可能不能识别10个不同的值,而将这些优先级进行每两个或多个合并,变成少于10个的优先级,则两个或多个优先级的线程可能被映射为一个优先级。
线程默认优先级是5,Thread类中有三个常量,定义线程优先级范围:
static int MAX_PRIORITY
线程可以具有的最高优先级。
static int MIN_PRIORITY
线程可以具有的最低优先级。
static int NORM_PRIORITY
分配给线程的默认优先级。
3、Thread.yield()方法
Thread.yield()方法作用是:暂停当前正在执行的线程对象,并执行其他线程。
yield()应该做的是让当前运行线程回到可运行状态,以允许具有相同优先级的其他线程获得运行机会。因此,使用yield()的目的是让相同优先级的线程之间能适当的轮转执行。但是,实际中无法保证yield()达到让步目的,因为让步的线程还有可能被线程调度程序再次选中。
结论:yield()从未导致线程转到等待/睡眠/阻塞状态。在大多数情况下,yield()将导致线程从运行状态转到可运行状态,但有可能没有效果。
运行结果:
4、join()方法
Thread的非静态方法join()让一个线程B“加入”到另外一个线程A的尾部。在A执行完毕之前,B不能工作。例如:
Thread t = new MyThread();
t.start();
t.join();
另外,join()方法还有带超时限制的重载版本。 例如t.join(5000);则让线程等待5000毫秒,如果超过这个时间,则停止等待,变为可运行状态。
线程的加入join()对线程栈导致的结果是线程栈发生了变化,当然这些变化都是瞬时的
执行结果:
由上图可以看出:
线程的状态转换是线程控制的基础。线程状态总的可分为五大状态:分别是生、死、可运行、运行、等待/阻塞。
各个状态细节不再赘述,具体请看/article/1325091.html此篇博文
此篇博文主要讲述各个状态的实例!
一、新建和就绪状态
此处不做细致的讲解,在上一篇博文中已经讲解过了!
package com.haixu.thread2; public class InvokeRun extends Thread{ /* * 新建就绪状态练习 * 通过继承Thread类来创建线程 * */ private int i; public void run(){ for(; i<5; i++){ //直接调用run()方法是,Thread的this.getName()返回的是该对象的名字 //使用Thread.currentThread().getName()调用当前线程获取线程名字 System.out.println(Thread.currentThread().getName() + " " + i); } } public static void main(String[] args) { for(int i=0; i<4; i++){ //使用Thread.currentThread().getName()调用当前线程获取线程名字 System.out.println(Thread.currentThread().getName() + " " + i); if(i == 3){ new InvokeRun().start(); //直接调用线程对象的run()方法 new InvokeRun().run(); new InvokeRun().start(); } } } }
二、阻止线程执行
堵塞状态是前述四种状态中最有趣的,值得我们作进一步的探讨。线程被堵塞可能是由下述五方面的原因造成的:
(1) 调用sleep(毫秒数),使线程进入“睡眠”状态。在规定的时间内,这个线程是不会运行的。
(2) 用suspend()暂停了线程的执行。除非线程收到resume()消息,否则不会返回“可运行”状态。
(3) 用wait()暂停了线程的执行。除非线程收到nofify()或者notifyAll()消息,否则不会变成“可运行”(是的,这看起来同原因2非常相象,但有一个明显的区别是我们马上要揭示的)。
(4) 线程正在等候一些IO(输入输出)操作完成。
(5) 线程试图调用另一个对象的“同步”方法,但那个对象处于锁定状态,暂时无法使用。
解决上面的堵塞,让线程重新进入就绪状态:
1、调用sleep()方法的的线程经过的指定的时间。
2、线程调用的阻塞式IO方法返回、
3、线程成功的获得了试图取得的同步监视器。
4、线程正在等待某个通知,其他线程发出了一个通知。
5、处于挂起状态的线程被调用了resume()恢复方法。
注意:
1、线程睡眠是帮助所有线程获得运行机会的最好方法。
2、线程睡眠到期自动苏醒,并返回到可运行状态,不是运行状态。sleep()中指定的时间是线程不会运行的最短时间。因此,sleep()方法不能保证该线程睡眠到期后就开始执行。
3、sleep()是静态方法,只能控制当前正在运行的线程。
/** * 一个计数器,计数到100,在每个数字之间暂停1秒,每隔10个数字输出一个字符串 * * @author leizhimin 2008-9-14 9:53:49 */ public class MyThread extends Thread { public void run() { for (int i = 0; i < 100; i++) { if ((i) % 10 == 0) { System.out.println("-------" + i); } System.out.print(i); try { Thread.sleep(1); System.out.print(" 线程睡眠1毫秒!\n"); } catch (InterruptedException e) { e.printStackTrace(); } } } public static void main(String[] args) { new MyThread().start(); } }
1、睡眠
Thread.sleep(long millis)和Thread.sleep(long millis, int nanos)静态方法强制当前正在执行的线程休眠(暂停执行),以“减慢线程”。当线程睡眠时,它入睡在某个地方,在苏醒之前不会返回到可运行状态。当睡眠时间到期,则返回到可运行状态。
线程睡眠的原因:线程执行太快,或者需要强制进入下一轮,因为Java规范不保证合理的轮换。
睡眠的实现:调用静态方法。
try {
Thread.sleep(123);
} catch (InterruptedException e) {
e.printStackTrace();
}
睡眠的位置:为了让其他线程有机会执行,可以将Thread.sleep()的调用放线程run()之内。这样才能保证该线程执行过程中会睡眠。
package com.haixu.thread2; import java.util.Date; public class SleepTest { public static void main(String[] args) throws Exception{ for( int i=0; i<5; i++){ System.out.println("当前的时间:" + new Date()); Thread.sleep(1000); } } }
运行结果:
当前的时间:Sun May 31 17:59:25 CST 2015 当前的时间:Sun May 31 17:59:26 CST 2015 当前的时间:Sun May 31 17:59:27 CST 2015 当前的时间:Sun May 31 17:59:28 CST 2015 当前的时间:Sun May 31 17:59:29 CST 2015
2、线程的优先级和线程让步yield()
线程的让步是通过Thread.yield()来实现的。yield()方法的作用是:暂停当前正在执行的线程对象,并执行其他线程。
要理解yield(),必须了解线程的优先级的概念。线程总是存在优先级,优先级范围在1~10之间。JVM线程调度程序是基于优先级的抢先调度机制。在大多数情况下,当前运行的线程优先级将大于或等于线程池中任何线程的优先级。但这仅仅是大多数情况。
注意:当设计多线程应用程序的时候,一定不要依赖于线程的优先级。因为线程调度优先级操作是没有保障的,只能把线程优先级作用作为一种提高程序效率的方法,但是要保证程序不依赖这种操作。
当线程池中线程都具有相同的优先级,调度程序的JVM实现自由选择它喜欢的线程。这时候调度程序的操作有两种可能:一是选择一个线程运行,直到它阻塞或者运行完成为止。二是时间分片,为池内的每个线程提供均等的运行机会。
设置线程的优先级:线程默认的优先级是创建它的执行线程的优先级。可以通过setPriority(int newPriority)更改线程的优先级。例如:
Thread t = new MyThread();
t.setPriority(8);
t.start();
线程优先级为1~10之间的正整数,JVM从不会改变一个线程的优先级。然而,1~10之间的值是没有保证的。一些JVM可能不能识别10个不同的值,而将这些优先级进行每两个或多个合并,变成少于10个的优先级,则两个或多个优先级的线程可能被映射为一个优先级。
线程默认优先级是5,Thread类中有三个常量,定义线程优先级范围:
static int MAX_PRIORITY
线程可以具有的最高优先级。
static int MIN_PRIORITY
线程可以具有的最低优先级。
static int NORM_PRIORITY
分配给线程的默认优先级。
3、Thread.yield()方法
Thread.yield()方法作用是:暂停当前正在执行的线程对象,并执行其他线程。
yield()应该做的是让当前运行线程回到可运行状态,以允许具有相同优先级的其他线程获得运行机会。因此,使用yield()的目的是让相同优先级的线程之间能适当的轮转执行。但是,实际中无法保证yield()达到让步目的,因为让步的线程还有可能被线程调度程序再次选中。
结论:yield()从未导致线程转到等待/睡眠/阻塞状态。在大多数情况下,yield()将导致线程从运行状态转到可运行状态,但有可能没有效果。
package com.haixu.thread2; /* * 使用Yield对线程让步,是正在执行的线程停止 * */ public class YieldTest extends Thread{ public YieldTest(String name){ super(name); } //定义run()作为执行体 public void run(){ for( int i=0; i<6; i++){ System.out.println(getName() + " " + i); //当i=4时线程做出让步 if(i==4){ Thread.yield(); } } } public static void main(String[] args) { //启动两个并发线程 YieldTest yt = new YieldTest("高级"); yt.setPriority(Thread.MAX_PRIORITY); yt.start(); //将此线程设置成低权限 YieldTest yt1 = new YieldTest("低级"); yt1.setPriority(Thread.MIN_PRIORITY); yt1.start(); } }
运行结果:
高级 0 高级 1 高级 2 高级 3 低级 0 低级 1 低级 2 低级 3 高级 4 高级 5 低级 4 低级 5
4、join()方法
Thread的非静态方法join()让一个线程B“加入”到另外一个线程A的尾部。在A执行完毕之前,B不能工作。例如:
Thread t = new MyThread();
t.start();
t.join();
另外,join()方法还有带超时限制的重载版本。 例如t.join(5000);则让线程等待5000毫秒,如果超过这个时间,则停止等待,变为可运行状态。
线程的加入join()对线程栈导致的结果是线程栈发生了变化,当然这些变化都是瞬时的
package com.haixu.thread2; /* * 当调用其他线程的join方法时,调用线程被阻塞 * 直到被join方法加入的线程执行完为止 * */ public class JoinTest extends Thread{ //提供有参数的构造器,用于设置线程的名称 public JoinTest(String name){ super(name); } public void run(){ for(int i = 0; i<6; i++){ System.out.println(getName() + " " + i); } } public static void main(String[] args) throws InterruptedException { //启动子线程 new JoinTest("新线程").start(); for(int i=0; i< 5; i++){ if(i == 2){ JoinTest jt = new JoinTest("被Join的线程"); jt.start(); //main线程调用了jt线程的join方法,main线程 //必须等jt之行结束才会向下执行 jt.join(); } System.out.println(Thread.currentThread().getName() + " " + i); } } }
执行结果:
main 0 main 1 新线程 0 新线程 1 新线程 2 新线程 3 新线程 4 新线程 5 被Join的线程 0 被Join的线程 1 被Join的线程 2 被Join的线程 3 被Join的线程 4 被Join的线程 5 main 2 main 3 main 4
相关文章推荐
- Java获取路径方法&相对路径读取xml文件方法
- 用Struts2标签实现Map的迭代
- JAVA反编译工具jad
- Spring2.5学习1.3_三种实例化bean的方式
- struts2表单提交的乱码的问题的解决
- eclipse ctrl + H 搜索问题 来自好搜
- 出现java.lang.NoSuchFieldException resourceEntries错误的解决方法
- Spring2.5学习1.4_bean的作用域及其实例化时期
- 用一重循环实现排序java
- Myeclipse2014破解教程及软件下载
- 打造Ubuntu下Java开发环境
- 用递归的思想写杨辉三角程序
- Spring aop--几种不同的使用方式
- Eclipse修改黑色主题的方法
- Java Struts2 的请求处理流程详解
- java基础之守护线程
- JAVA 子父类的特点
- Java笔记--枚举&注解
- java内存划分
- java--回调方法