join线程
Thread提供了让一个线程等待另一个线程完成的方法:join()方法。当在某个程序执行流中调用其他线程的join()方法
时,调用线程将被阻塞,知道被join方法加入的join线程完成为止。
join()方法通常由使用线程的程序调用,以将大问题划分为许多小问题。如下面程序:
时,调用线程将被阻塞,知道被join方法加入的join线程完成为止。
join()方法通常由使用线程的程序调用,以将大问题划分为许多小问题。如下面程序:
- public class JoinThread extends Thread{
- /***
- * 提供一个有参数的构造器,用于设置该线程的名字
- * @param name
- */
- public JoinThread(String name){
- super(name);
- }
- @Override
- public void run() {
- super.run();
- for(int i = 0;i<100;i++){
- System.out.println(getName() + " " + i);
- }
- }
- public static void main(String[] args) {
- new JoinThread("新线程").start();
- for(int i=0;i<100;i++){
- if(i == 20){
- JoinThread jt = new JoinThread("被Join的线程");
- jt.start();
- //main线程调用了jt线程的join方法,main线程必须等待jt执行结束之后才会向下执行
- try {
- jt.join();
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- System.out.println(Thread.currentThread().getName() + " " + i);
- }
- }
- }
- }
上面程序中一共有3条线程,主方法开始时就启动了名为“新线程”的子线程,该子线程将会和main线程并发执行。当
主线程的循环变量i为20时,启动了名为“被join的线程”的线程,该线程不会和main线程并发执行,而是main线程必须等待
该线程执行结束之后才可以向下执行。
join方法有3中重载的方法
== join():等待被join的线程执行完成
== join(long millis):等待被join的线程的时间最长为millis毫秒。如果在millis毫秒内,被join的线程还没有
执行结束则不再等待。
== join(long millis,int nanos);等待被join的线程的时间最长为millis毫秒加上nanos微秒(千分之一毫秒)。
通常很少使用第三个方法,原因有两个:程序对时间的精度无须精确到千分之一毫秒!计算机硬件、操作系统本身
也无法精确到千分之一毫秒。
后台线程
有一种线程,它是后台运行的,它的任务是为其他的线程提供服务,这种线程被成为“后台线程”,又称为“守护线程”。
JVM的垃圾回收线程是典型的后台线程。
后台线程有个特征:如果所有的前台线程都死亡,后台线程会自动死亡。
调用Thread对象的setDaemon(true)方法可将指定线程设置为后台线程。
JVM的垃圾回收线程是典型的后台线程。
后台线程有个特征:如果所有的前台线程都死亡,后台线程会自动死亡。
调用Thread对象的setDaemon(true)方法可将指定线程设置为后台线程。
线程睡眠:sleep
如果我们需要让当前正在执行的线程暂停一段时间,并进入阻塞状态,则可以通过调用Thread类的静态sleep方法,
sleep方法有两种重载的方式:
static void sleep(long millis):让当前正在执行的线程暂停millis毫秒,并进入阻塞状态,该方法受到系统计时
器和线程调度器的精度和准确度的影响。
static void sleep(long millis,int nanos):让当前正在执行的线程暂停milis毫秒加nonas毫微秒,并进入阻塞
状态,该方法受到系统计时器和线程调度器的精度和准确度的影响。
与前面类似的是,程序很少调用第二种方法。
当当前线程调用sleep方法进入阻塞状态后,其在sleep时间段内,该线程不会获得执行的机会,即使系统中没有其他
可运行的线程,处于sleep中的线程也不会运行,因此sleep方法常用来暂停程序的执行。
下面程序调用sleep方法来暂停主线程的执行,因为该程序只有一条主线程,当主线程进入sleep后,系统没有可执行的
线程,所以可以看到程序在sleep处暂停。
sleep方法有两种重载的方式:
static void sleep(long millis):让当前正在执行的线程暂停millis毫秒,并进入阻塞状态,该方法受到系统计时
器和线程调度器的精度和准确度的影响。
static void sleep(long millis,int nanos):让当前正在执行的线程暂停milis毫秒加nonas毫微秒,并进入阻塞
状态,该方法受到系统计时器和线程调度器的精度和准确度的影响。
与前面类似的是,程序很少调用第二种方法。
当当前线程调用sleep方法进入阻塞状态后,其在sleep时间段内,该线程不会获得执行的机会,即使系统中没有其他
可运行的线程,处于sleep中的线程也不会运行,因此sleep方法常用来暂停程序的执行。
下面程序调用sleep方法来暂停主线程的执行,因为该程序只有一条主线程,当主线程进入sleep后,系统没有可执行的
线程,所以可以看到程序在sleep处暂停。
- public class TestSleep {
- public static void main(String[] args) throws Exception{
- for(int i = 0;i<100;i++){
- System.out.println("当前时间:" + new Date());
- Thread.sleep(1000);
- }
- }
- }
线程让步:yield
yield()方法是一个和sleep方法有点相似的方法,它也是一个Thread类提供的一个静态方法,它也可以让当前正在执行
的线程暂停,但它不会阻塞该线程,它只是将该线程转入就绪状态。yield只是让当前线程暂停一下,让系统的线程调度器
重新调度一次,完全可能的情况是:当某个线程调用了yield方法暂停之后,线程调度器又将其调度出来重新执行。
实际上,当某个线程调用了yield方法暂停之后,只有优先级与当前线程相同,或者优先级比当前线程更高的就绪状态的
线程才会得到执行的机会。下面程序使用yield来让当前正在执行的线程暂停:
上面程序中的代码把设置优先级的两行给注释掉了,运行的效果图如下图:
的线程暂停,但它不会阻塞该线程,它只是将该线程转入就绪状态。yield只是让当前线程暂停一下,让系统的线程调度器
重新调度一次,完全可能的情况是:当某个线程调用了yield方法暂停之后,线程调度器又将其调度出来重新执行。
实际上,当某个线程调用了yield方法暂停之后,只有优先级与当前线程相同,或者优先级比当前线程更高的就绪状态的
线程才会得到执行的机会。下面程序使用yield来让当前正在执行的线程暂停:
- public class TestYield extends Thread{
- public TestYield(){
- }
- public TestYield(String name){
- super(name);
- }
- @Override
- public void run() {
- super.run();
- for(int i = 0;i<50;i++){
- System.out.println(getName() + " " + i);
- //当i=20的时候,使用yield方法让当前线程让步
- if(i == 20){
- Thread.yield();
- }
- }
- }
- public static void main(String[] args) throws Exception{
- //启动两条并发线程
- TestYield ty1 = new TestYield("高级");
- //将ty1线程设置成最高优先级
- //ty1.setPriority(Thread.MAX_PRIORITY);
- ty1.start();
- TestYield ty2 = new TestYield("低级");
- //将ty2线程设置成最高优先级
- //ty2.setPriority(Thread.MIN_PRIORITY);
- ty2.start();
- }
- }
上面程序中的代码把设置优先级的两行给注释掉了,运行的效果图如下图:
反之,如果我们把那两行代码的注释取消,运行的效果图如下图:
关于sleep方法和yield方法的区别如下:
== sleep方法暂停当前线程后,会给其他线程执行机会,不会理会其他线程的优先级。但yield方法只会给优先级相同,
或优先级更高的线程执行机会。
== sleep方法会将线程转入阻塞状态,直到经过阻塞时间才会转入就绪状态。而yield不会将线程转入阻塞状态,它只是
强制当前线程进入就绪状态。因此完全可能某个线程调用yield方法暂停之后,立即再次获得处理器资源被执行。
== sleep方法声明抛出了InterruptedException异常,所以调用sleep方法时要么捕捉该异常,要么显示声明抛出该异常。
而yield方法则没有声明抛出任何异常。
== sleep方法比yield方法更好的可移植性,通常不要依靠yield来控制并发线程的执行。
== sleep方法暂停当前线程后,会给其他线程执行机会,不会理会其他线程的优先级。但yield方法只会给优先级相同,
或优先级更高的线程执行机会。
== sleep方法会将线程转入阻塞状态,直到经过阻塞时间才会转入就绪状态。而yield不会将线程转入阻塞状态,它只是
强制当前线程进入就绪状态。因此完全可能某个线程调用yield方法暂停之后,立即再次获得处理器资源被执行。
== sleep方法声明抛出了InterruptedException异常,所以调用sleep方法时要么捕捉该异常,要么显示声明抛出该异常。
而yield方法则没有声明抛出任何异常。
== sleep方法比yield方法更好的可移植性,通常不要依靠yield来控制并发线程的执行。
改变线程的优先级
每个线程执行时都具有一定的优先级,优先级高的线程获得较多的执行机会,而优先级低的线程则获得较少的执行机会。
每个线程默认的优先级都与创建它的父线程具有相同的优先级,在默认情况下,main线程具有普通优先级,由main线程
创建的子线程也有普通优先级。
Thread提供了setPriority(int newPriority)和getPriority()方法来设置和返回指定线程的优先级,其中setPriority
方法的参数可以是一个整数,范围是1~10之间,也可以使Thread类的三个静态变量:
== MAX_PRIORITY:其值时10.
== MIN_PRIORITY:其值是1.
== NORM_PRIORITY:其值是5.
下面程序使用了setPriority来改变主线程的优先级,并使用了该方法改变了两条线程的优先级,从而可以看到高优先级
的线程将的到更多的执行机会。
每个线程默认的优先级都与创建它的父线程具有相同的优先级,在默认情况下,main线程具有普通优先级,由main线程
创建的子线程也有普通优先级。
Thread提供了setPriority(int newPriority)和getPriority()方法来设置和返回指定线程的优先级,其中setPriority
方法的参数可以是一个整数,范围是1~10之间,也可以使Thread类的三个静态变量:
== MAX_PRIORITY:其值时10.
== MIN_PRIORITY:其值是1.
== NORM_PRIORITY:其值是5.
下面程序使用了setPriority来改变主线程的优先级,并使用了该方法改变了两条线程的优先级,从而可以看到高优先级
的线程将的到更多的执行机会。
- public class PriorityThread extends Thread{
- public PriorityThread(){
- }
- public PriorityThread(String name){
- super(name);
- }
- @Override
- public void run() {
- super.run();
- for(int i =0;i<50;i++){
- System.out.println(getName() + ",其优先级是:" + getPriority() + ",循环变量的值为:" + i);
- }
- }
- public static void main(String[] args) {
- //改变主线程的优先级
- Thread.currentThread().setPriority(6);
- for(int i= 0;i<30;i++){
- if(i == 10){
- PriorityThread low = new PriorityThread("低级");
- low.start();
- System.out.println("创建之初的优先级:" + low.getPriority());
- //设置该线程为最低优先级
- low.setPriority(Thread.MIN_PRIORITY);
- }
- if(i == 20){
- PriorityThread high = new PriorityThread("高级");
- high.start();
- System.out.println("创建之初的优先级:" + high.getPriority());
- //设置该线程优先级为最高级
- high.setPriority(Thread.MAX_PRIORITY);
- }
- }
- }
- }