这篇文章接着上篇文章<<java 线程简介>> 写的.

http://blog.csdn.net/nvd11/article/details/19118683

上一篇文章提到,  java程序猿可以利用类Thread或其接口Runnable开启一条新线程.

但开启一条新线程之后, 不能任由它不管啊.

其实java有很多方法让程序猿控制线程的执行过程.

一, 线程的三种状态切换

一个线程由线程类Thread或其派生类的start()启动.

启动后的线程具有3种状态.

1.1 就绪状态

注意, 1个线程t被t.start()函数启动后, 并不是立即可以被cpu执行. 而是进入了就绪状态.

在就绪状态中的线程代表了有了cpu执行的资格. 由于想抢占cpu被执行的线程有很多. cpu并不一定立即执行t对应的线程.

实际上, 一般情况下在1个时间点, cpu只会执行1个线程, 但是这个cpu会不断跳到另1个线程去执行它. 前提是这个"另1个进程是"就绪状态.

1.2 运行状态

相对地, cpu当前执行进程所在的状态就是执行状态.

一般来讲, 一个程序运行中同1个时间点只会有1条线程处于运行状态.

但是cpu会不断地切换所执行的线程.  一旦cpu切换到另1个线程执行. 原来运行的线程就会从运行状态切换到就绪状态.

这种行为我们就称为cpu的调度.

1.3 阻塞状态.

一旦1个线程遇到阻塞事件(例如sleep()函数), (无论它原来是在运行状态还是就绪状态),就会进入阻塞状态.

处于阻塞状态的线程无法被cpu调度, 也就是无法被cpu执行(进入运行状态).

直接阻塞接触后, 该线程返回就绪状态.

简单图示如下:

1.4 现实例子

假如哟有A, B, C 三个人都想使用另1个人D都电脑上网,  但是D只有1台电脑。

然后D就让A B C 三人首先坐在厅里都长沙发上, 然后D挑选1个人进房间使用电脑。

每隔一段时间D会把使用电脑都人赶回厅里都沙发上, 然后再从沙发上挑1个人进去使用电脑。

那么3个人就都有机会使用电脑, 令网络上觉得A B C3个人有电脑都假象。

1.那么在房间使用电脑都人就相对于多线程的运行状态。

2.坐在沙发的人就相当于多线程的就绪状态, 他们都有机会被D选中使用电脑.

3.如过A突然肚子痛(阻塞事件)离开了沙发, 那么A就相当于进入了线程的阻塞状态, 除非A再次返回沙发上, 才会有资格被D选中。

二, 线程的优先级设置

一般来讲, 假如有两条线程再执行, cpu是会随机切换执行的。

但是实际上线程也有重要性的区别,我们可以利用优先级别设置来控制某1个线程更有机会抢占cpu资源。

Java提供1个线程调度器来监控程序中启动后进入就绪状态都所有线程。

线程调度器挑选执行线程的机制受到线程优先级的影响。

2.1 类Thread 的3个静态常量成员

线程的优先级用数字表示, 范围从1到10, 1个线程都默认优先级是5。

这个数字越大表示线程都优先级越高

类Thread还提供了3个静态常量成员, 提供给程序猿,可以代替数字来使用。

他们分别是

Thread.MIN_PRIORITY=1

Thread.MAX_PRIORITY=10

Thread.NORM_PRIORITY=5

注意, 这个3个成员都是static final的. 代表都是它们都属于类本身,而且不能被赋值。

2.2 线程对象获取和设置优先级的方法。

2.2.1 int getPriority();

调用这个方法可以获得对应线程对象都优先级。 例如 t.getPriority() 返回都就是线程对象t都当前优先级。

2.2.2 void setPriority(int newPriority)

线程对象可以调用这个方法来改变本身都优先级.

注意,参数范围必须再1-10, 否则会抛出java.lang.IllegalArgumentException 异常。

2.3 设置优先级都一个例子

package Thread_kng.Td_priority_kng;class M_thrd_6 implements Runnable{public void run(){int i;Thread cur_thd = Thread.currentThread();for (i=1; i<101; i++){System.out.printf("Thread %s: priority is %d, i is %d\n", cur_thd.getName(),cur_thd.getPriority(),i);}}
}
public class Td_priority_1{public static void f(){M_thrd_6 s = new M_thrd_6();Thread t1 = new Thread(s);Thread t2 = new Thread(s);t1.setName("T1");t2.setName("T2");t1.start();t1.setPriority(Thread.MIN_PRIORITY);  //set the priority to 1t2.setPriority(Thread.NORM_PRIORITY + 3); //set the priority to 8t2.start();}
}

看上面的例子:

我利用实验Runnable 接口的1个对象构造两个子线程。

在run方法里。

这两个子线程(T1, T2)循环100次把i输出到屏幕上, 并顺便输出线程的名字,和线程都当前优先级(getPriority())都输出到屏幕。

看例子下面的f()函数, 首先执行的是t1. 再执行的是t2.

但是t1的优先级别调低了, t2的优先级别调高了。

结果就是T2比T1先执行完成。

执行结果:

2.4 优先级对线程调度的真正影响

见到结果中,虽然T2的优先级比T1高得多, 但是T2和T1实际上还是不断被切换执行的。

2.4.1 时间片论算法。

所谓时间片论算法就是指java中1个线程处于执行状态的单次时间是一定的。

所以即使T2的优先级比T1高, 并不是指T2处于单次执行状态的时间比较长。

假如单次执行状态的时间是x毫秒,

那么无论T2还是T1,被执行x毫秒后,都会被强制退回就绪状态, cpu再从就绪状态的线程选1个使其进入执行状态。

而优先级别高的线程被选中的机率相对更高

那么单位时间内, T2进入执行状态的次数会比T1高。

实际上, 这个单次执行时间比输出100次i的时间小得多, 所以上面运行例子会见到T1 和 T2不断切换执行。

2.4.2 实际开发中,java并不单纯依赖优先级别来决定线程执行次序。

也就是讲, 优先级别只是影响cpu调度的其中1个因数。

还有如下其他因数:

1. 突发事件, 例如打印机的打印线程发现打印机的纸张用完, 那么系统就会马上执行打印线程的警告机制。

2. 线程执行时间, 通常会把消耗资源小,执行快的线程放在前面运行。

3. 最长等待时间, 一个线程即使优先级别很少, 但是超过了一段等待时间后, 会被强制进入执行状态。

2.4 现实例子

举回上面A B C三个人上网的时间, 优先级别就是A B C三个人跟D的个人关系了。

高优先级别的人每次再就绪状态中被D选中的机率更大, 但是每次进入房间上网的时间还是一样的。

三, 线程控制的常用方法

下面开始介绍线程的常用方法, 也是面试中问得比较多的地方:

3.1 sleep()

sleep(int n) 是1个常用的线程函数,参数s代表的是毫秒数字,他的作用是令线程停止执行并进入阻塞状态n毫秒。

在这n毫秒内, 这个线程不能被cpu执行。

过了n毫秒后, 这个线程重新进入就绪状态,但是不代表这个线程能马上被cpu执行。

上面说过了, 就绪状态的线程想要执行还需要取决于cpu的调度。

它在线程基类Thread中定义如下:

public static void sleep(long millis,
                         int nanos)
                  throws InterruptedException

在指定的毫秒数加指定的纳秒数内让当前正在执行的线程休眠(暂停执行),此操作受到系统计时器和调度程序精度和准确性的影响。该线程不丢失任何监视器的所属权。

参数:
        millis - 以毫秒为单位的休眠时间。
        nanos - 要休眠的另外 0-999999 纳秒。
    抛出:
        IllegalArgumentException - 如果 millis 值为负或 nanos 值不在 0-999999 范围内。
        InterruptedException - 如果任何线程中断了当前线程。当抛出该异常时,当前线程的中断状态 被清除

需要注意的是: sleep函数会抛出异常, 而且这个异常是非RuntimeException, 所以必须进行捕捉。

关于异常的文章里讲过, 捕捉要不进行try catch, 要不在函数定义中使用throws.

但是sleep()函数最终都是卸载线程类的run()方法里面的。 而基类Thread的run()方法是不抛出任何异常的。 所以由于多态的存在,重写的run()的方法不能throws任何异常.

所以一般在使用sleep函数时,必须使用try catch

下面是1个例子:

package Thread_kng.Td_ctrl;class M_thrd_7 implements Runnable{public void run(){java.text.DateFormat d1 = java.text.DateFormat.getDateTimeInstance();java.util.Date now;Thread cur_thd = Thread.currentThread();int i;for (i=0; i<10; i++){now = new java.util.Date();System.out.printf("Thread %s: i is %d, time is %s\n",cur_thd.getName(), i, d1.format(now));try{cur_thd.sleep(3000);   //sleep 3 seconds, must be put into the try{}}catch(Exception e){}}}
}public class Td_ctrl_1{public static void f(){M_thrd_7 s = new M_thrd_7();Thread t1 = new Thread(s);t1.setName("T1");t1.start();Thread t2 = new Thread(s);t2.setName("T2");t2.start();}
}

这个例子的线程业务很简单, 就是把i从9输出到0, 但是每输出1次暂停3000毫秒。

需要注意的是,sleep()函数是类Thread的一个成员方法, 所以使用sleep()方法的前提是1个Thread类或其派生类的一个对象。

在下面的f()方法中,利用1个对象创建了两个线程t1.t2并启动。

结果就是t1 和 t2两条线程都是每3秒在屏幕输出一次信息:

输出结果:

Thread T1: i is 0, time is Feb 17, 2014 2:42:10 PM
Thread T2: i is 0, time is Feb 17, 2014 2:42:10 PM
Thread T1: i is 1, time is Feb 17, 2014 2:42:13 PM
Thread T2: i is 1, time is Feb 17, 2014 2:42:13 PM
Thread T1: i is 2, time is Feb 17, 2014 2:42:16 PM
Thread T2: i is 2, time is Feb 17, 2014 2:42:16 PM
Thread T1: i is 3, time is Feb 17, 2014 2:42:19 PM
Thread T2: i is 3, time is Feb 17, 2014 2:42:19 PM
Thread T1: i is 4, time is Feb 17, 2014 2:42:22 PM
Thread T2: i is 4, time is Feb 17, 2014 2:42:22 PM
Thread T1: i is 5, time is Feb 17, 2014 2:42:25 PM
Thread T2: i is 5, time is Feb 17, 2014 2:42:25 PM
Thread T1: i is 6, time is Feb 17, 2014 2:42:28 PM
Thread T2: i is 6, time is Feb 17, 2014 2:42:28 PM
Thread T1: i is 7, time is Feb 17, 2014 2:42:31 PM
Thread T2: i is 7, time is Feb 17, 2014 2:42:31 PM
Thread T1: i is 8, time is Feb 17, 2014 2:42:34 PM
Thread T2: i is 8, time is Feb 17, 2014 2:42:34 PM
Thread T1: i is 9, time is Feb 17, 2014 2:42:37 PM
Thread T2: i is 9, time is Feb 17, 2014 2:42:37 PM
gateman@TPEOS classes $

sleep() 是1个静态方法, 一般来讲直接调用Thread.sleep(x),  就可以令当前执行的线程暂停x毫秒. 并不需要获得当前的线程对象.

3.2 yield()

yield方法是基类Thread的另1个成员方法。

在中文JDK的解析是这样的:

public static void yield()
暂停当前正在执行的线程对象,并执行其他线程。

但是这个解释并不准确。

我们一般把yield方法简称为线程让步。

准确的解析如下:

当t线程执行t.yield() 后,会马上停止t的执行状态, 并且令t退回到就绪状态。

1. yield方法没有参数。

2. yield 会令线程的执行状态终止。

3. yield 会令线程退回到就绪状态。

4. 然后cpu还重新调度, 选择一个线程进入执行状态,  注意这个线程有可能是刚才yield退回就绪状态的线程。

5. 所以java jdk api 中午的解析是不准确的。并不是暂停线程, 并执行其他线程, 而是暂停线程, 重新让cpu调度。

也就是说1个线程执行yield后被退回就绪状态, 如果它的优先级别高, 它是有可能马上被cpu重新执行进行执行状态的。

那么yield()的意义是什么呢, 本屌也不是很sure,  但是如果1个线程,不断循环执行1个包含yield的方法, 那么每一次循环它都会让步一次。

间接地令到这个线程比其他不含yield的线程更低。

下面是1个例子:

package Thread_kng.Td_ctrl;class M_thrd_8 extends Thread{public M_thrd_8(String name){super(name);}public void run(){int i;for (i=0; i<1000; i++){System.out.printf("Thread %s: i is %d",this.getName(), i);this.yield();}}
}class M_thrd_9 extends Thread{public M_thrd_9(String name){super(name);}public void run(){int i;for (i=0; i<1000; i++){System.out.printf("\nThread %s: i is %d\n",this.getName(), i);//this.yield();}}
}public class Td_yield_1{public static void f(){M_thrd_8 t1 = new M_thrd_8("T1");t1.start();M_thrd_9 t2 = new M_thrd_9("T2");t2.start();}
}

上面例子定义两个基本相同的线程类, 都是循环把i 从0 输出到 999。

但是t1 线程每1个循环都yield让步一次, t2 线程没有。

执行时, t2会比t1得到更多cpu资源。

如果这个两个线程属于不同进程, 那么具有yield方法的进程cpu占用率会稍低。

对于这个例子来说, 结果就是t2执行得比t1快, 而且比单纯地设置优先级别明显得多.

执行结果:

Thread T2: i is 956Thread T2: i is 957Thread T2: i is 958Thread T2: i is 959Thread T2: i is 960Thread T2: i is 961Thread T2: i is 962
Thread T1: i is 349
Thread T2: i is 963Thread T2: i is 964Thread T2: i is 965
Thread T1: i is 350
Thread T2: i is 966Thread T2: i is 967Thread T2: i is 968
Thread T1: i is 351
Thread T2: i is 969Thread T2: i is 970
Thread T1: i is 352
Thread T2: i is 971

3.3 sleep() 和 yield()的区别

我们还是举回上面A B C三人用D的电脑上网的例子:

sleep()函数必须有个时间参数, 加入线程A执行了sleep(n),就相当于A有事去厕所, 时间是n。 这段时间内A处于阻塞状态, 无法被D选中去上网的。

过了n时间后, A回来到沙发上进入就绪状态, 但是还是需要D的调度才能去上网。

而yield方法就相当于正在上网的A被D拉回到沙发上, 然后重新等待D的调度, 但是有可能D还是选中A的, 也就是说A被拉回到沙发上, 但是马上又可以去上网。

3.4 join()

join() 是基类Thread的另1个成员方法。

JDK API 是如此定义的:

public final void join()
                throws InterruptedException
等待该线程终止

详细的定义是:

当线程t 调用t.join() 时, 暂停当前线程的执行, 除非t执行完成了, 当前线程继续执行。

注意当前线程是执行t.join()的线程。

现实例子:

又是A B C三人上网的例子, 假如B线程执行A.join() , 也相当于B告诉D,我离开一会, 等A上完网时我就才回来上网。

注意,C不受影响哦, 实际上就是B 1个人暂时退出, A和C两人简单切换上网, 直至A上完网, B才回来就绪状态!

值得注意的是, join()类似sleep()方法, 都会抛出异常。

下面是1个join()方法的例子:

package Thread_kng.Td_ctrl;class M_thrd_10 extends Thread{public M_thrd_10(String name){super(name);}也就是说t3必须等t1完成才能执行public void run(){int i;for (i=0; i<1000; i++){System.out.printf("Thread %s: i is %d\n",this.getName(), i);}}
}class M_thrd_11 extends Thread{private M_thrd_10 t_join;public M_thrd_11(String name, M_thrd_10 t_join){super(name);this.t_join = t_join;}public void run(){int i;for (i=0; i<501; i++){System.out.printf("Thread %s: i is %d\n",this.getName(), i);}try{t_join.join();   }catch(Exception e){}for (; i<1000; i++){System.out.printf("Thread %s: i is %d\n",this.getName(), i);}}
}public class Td_join_1{public static void f(){M_thrd_10 t1 = new M_thrd_10("T1"); M_thrd_10 t2 = new M_thrd_10("T2"); //will not impacted by t1.join()M_thrd_11 t3 = new M_thrd_11("T3",t1);t1.start();t3.start();t2.start();}
}

上面定义了两个线程类, 实例化了3个对象t1, t2, t3, 而t3的run()方法里, 调用了t1.join()。

也就是说t1. t2都把i从0输出到999

但是t3先输出到500 就必须等t1完成, 才继续输出501 到 999

也就是说t3必须等t1完成才能执行

但是t2是不受影响的。

执行结果:

Thread T1: i is 981
Thread T1: i is 982
Thread T1: i is 983
Thread T1: i is 984
Thread T1: i is 985
Thread T1: i is 986
Thread T1: i is 987
Thread T1: i is 988
Thread T1: i is 989
Thread T1: i is 990
Thread T1: i is 991
Thread T1: i is 992
Thread T1: i is 993
Thread T1: i is 994
Thread T1: i is 995
Thread T1: i is 996
Thread T1: i is 997
Thread T1: i is 998
Thread T1: i is 999
Thread T2: i is 899
Thread T3: i is 501
Thread T3: i is 502
Thread T3: i is 503
Thread T3: i is 504
Thread T3: i is 505
Thread T3: i is 506
Thread T3: i is 507
Thread T3: i is 508
Thread T3: i is 509
Thread T3: i is 510
Thread T3: i is 511
Thread T3: i is 512

3.5 wait() notify() notifyAll()

这个3个方法涉及同步的问题,  我会在以后介绍java同步的博文里再讲

Java里的线程控制相关推荐

  1. Java里阻塞线程的三种实现方法

    在日常开发中,我们有时会遇到遇到多线程处理任务的情况,JDK里提供了便利的 ThreadPoolExecutor以及其包装的工具类Executors.但是我们知道 ExecutorService.ex ...

  2. java里新建线程设置线程名字_多线程开发不得不掌握,设置和获取线程名称及JVM如何运行的...

    原标题:多线程开发不得不掌握,设置和获取线程名称及JVM如何运行的 欲善编程,多看.多敲.多讨论:动眼.动手.动大脑. 1 如何设置和获取线程名称 多线程的运行状态是不确定的,在程序开发过程中,想要获 ...

  3. java 协程线程的区别_为什么 Java 坚持多线程不选择协程?

    谢邀. 先说结论:协程是非常值得学习的概念,它是多任务编程的未来.但是Java全力推进这个事情的动力并不大. 先返回到问题的本源.当我们希望引入协程,我们想解决什么问题.我想不外乎下面几点:节省资源, ...

  4. 【转】Java里如何实现线程间通信

    正常情况下,每个子线程完成各自的任务就可以结束了.不过有的时候,我们希望多个线程协同工作来完成某个任务,这时就涉及到了线程间通信了. 本文涉及到的知识点:thread.join(), object.w ...

  5. Java 里的thread (线程)简介

    在Java里 thread 就是线程的意思. 说到线程的概念, 自然离不开另外两个词: 程序和进程. 从最基本的程序讲起: 一. 什么是程序(Program) 所谓程序, 就是1个严格有序的指令集合. ...

  6. Java笔记-多线程之线程控制

    线程控制 我们已经知道了线程的调度,接下来我们就可以使用如下方法对象线程进行控制. 1.线程休眠 public static void sleep(long millis):让当前线程处于暂停状态,m ...

  7. 深入理解 Java 锁与线程阻塞

    相信大家对线程锁和线程阻塞都很了解,无非就是 synchronized, wait/notify 等, 但是你有仔细想过 Java 虚拟机是如何实现锁和阻塞的呢?它们之间又有哪些联系呢?如果感兴趣的话 ...

  8. Java 多线程:线程优先级

    1 优先级取值范围 Java 线程优先级使用 1 ~ 10 的整数表示: 最低优先级 1:Thread.MIN_PRIORITY 最高优先级 10:Thread.MAX_PRIORITY 普通优先级 ...

  9. Java—这把线程池ThreadPoolExecutor操作,你学会了吗?

    关注微信公众号:CodingTechWork,一起学习进步. 引导 要求:线程资源必须通过线程池提供,不允许在应用自行显式创建线程: 说明:使用线程池的好处是减少在创建和销毁线程上所花的时间以及系统资 ...

最新文章

  1. 密码学是如何保护区块链的
  2. java分布式锁终极解决方案之 redisson
  3. Binder相关面试总结(六):四大组件底层的通信机制是怎样的
  4. python web为什么不火-Python这么火,为何有人说Python不好找工作?
  5. 初步认识Volatile-CPU高速缓存
  6. Faster\Slower 快慢指针的应用
  7. 以当天日期时间,打包目录
  8. 大楼通信综合布线系统_某办公大楼综合布线系统设计实例,小白可以借鉴一下,大神请绕路...
  9. Python调用C的方法
  10. Android官方开发文档Training系列课程中文版:支持不同的设备之支持不同的屏幕
  11. android 自定义海报,Android仿海报工厂(完)
  12. redis基础学习(一)— 配置项
  13. 在Python中从头开始迭代本地搜索
  14. 网络编程-----网络通信协议
  15. python合并excel工作簿_使用python将excel工作簿工作表合并为一个工作表
  16. 相机和镜头选型计算公式
  17. 个人整理的免费的Bootstrap模板
  18. 【linux内核分析与应用-陈莉君】设备驱动模型
  19. Grad-CAM:Visual Explanations from Deep Networks via Gradient-based Localization
  20. C语言 | 计算某日是该年的第几天

热门文章

  1. [How TO]-如何编写Linux kernel documentation
  2. redis 附近的人_Redis GEO地理位置信息,查看附近的人
  3. 使用tracee编写规则追踪系统安全事件
  4. Objective-c 静态变量的定义
  5. 【ElementUI】 table表格尾部的合计行,固定表头却不显示合计行
  6. 在Debian 上安装php zip扩展
  7. JavaScript实现轮播图
  8. 2021夏季每日一题 【week4 完结】
  9. PAT乙级全套超详细题解【建议收藏】
  10. AtomicInteger原子性