1 生命周期

  线程被创建和启动后(调用start()方法后),并不是已启动就进入执行状态,也不是一直处于执行状态。线程状态转换图如下: 
这里写图片描述

1.1 新建和就绪状态

  新建状态:当程序new了一个线程对象之后,该线程就处于新建状态。和其他java对象一样,仅仅有JVM为其分配内存,并初始化成员变量的值。 
  就绪状态:线程对象调用了start()方法后。JVM为其创建方法调用栈和程序计数器。此时,线程并没有开始运行,何时运行则取决于JVM里线程调度器的调度。

  注意:启动线程是start()方法,不是run()方法。直接调用run(),系统会把线程对象当成一个普通对象,而run()方法也是一个普通方法,而不是线程执行体。因此,在run()方法返回之前,其他线程无法并发执行。 
  如下测试代码:

package thread;/**
 * Created by Zen9 on 2016/3/4.
 */public class Runtest extends Thread{
    @Override
    public void run() {        for (int i = 0; i < 100; i++) {
            System.out.println(this.getName() + " " +i);
        }
    }    public static void main(String[] args) {        //创建两个新线程,并调用run()方法
        new Runtest().run();        new Runtest().run();        for (int i = 0; i < 100; i++) {
            System.out.println(Thread.currentThread().getName() + " " + i);
        }
    }
}12345678910111213141516171819202122

  程序则顺序地输出结果,变成了单线程。

1.2 运行和阻塞状态

  运行状态:处于就绪状态的线程获得了CPU,开始执行run()方法的线程执行体。 
  如果计算机只有一个CPU,那么在任何时刻只有一个线程处于运行状态。当然,在一个多处理器的机器上,将会有多个线程并行执行;当线程数大于处理器数时,依然会存在多个线程在同一个CPU上轮换的现象。 
   
线程进入阻塞状态的情况:

  • 调用sleep()方法主动放弃占用的处理器资源

  • 调用了一个阻塞式IO方法,在方法返回之前,线程被阻塞

  • 线程试图获取一个同步监视器,但该同步监视器正被其他线程所持有

  • 线程在等待某个通知(notify)

  • 调用了suspend()方法将线程挂起。(易导致死锁)

阻塞的线程重新进入就绪状态:

  • 调用的sleep()方法,过了指定时间

  • 调用的阻塞IO方法已经返回

  • 线程成功获得了同步监视器

  • 线程在等待通知时,其他线程发出了一个通知

  • 挂起状态的线程被调用了resume()恢复方法

1.3 线程终止

线程结束后,就处于终止状态:

  • run()或call()方法执行完成,线程正常结束。

  • 线程抛出一个未捕获的Exception或Error

  • 直接调用stop()方法结束线程。(易导致死锁)

2 控制线程

2.1 join线程

  join():让一个线程等待另一个线程完成。当某个程序执行流中调用了其他线程的join()方法时,调用线程将被阻塞,直到被join()方法加入的join线程执行完为止。

package thread;/**
 * Created by Zen9 on 2016/3/4.
 */public class JoinThreadTest extends Thread{
    //有参数的构造函数,设置线程名字
    public JoinThreadTest(String name){        super(name);
    }    @Override
    public void run() {        for (int i = 0; i < 100; i++) {
            System.out.println(getName()+":"+i);
        }
    }    public static void main(String[] args) throws Exception{        new JoinThreadTest("新线程").start();        for (int i = 0; i < 100; i++) {            if (i == 20){
                JoinThreadTest joinThreadTest = new JoinThreadTest("被Join的线程");
                joinThreadTest.start();                //调用joinThreadTest线程的join()方法,main线程则会被阻塞
                joinThreadTest.join();
            }
            System.out.println(Thread.currentThread().getName()+":"+i);
        }
    }
}1234567891011121314151617181920212223242526272829303132

运行结果: 
这里写图片描述

join()方法重载形式:

  • join():等待被join的线程执行完成

  • join(long millis):等待被join的线程的时间最长为millis毫秒。

2.2 后台线程

  有一种线程,它是后台运行的,它的任务是为其他的线程提供服务,这种线程被称为“后台线程(Daemon Thread)”,又称为“守护线程”或“精灵线程”。 
  特征:当所有的前台线程都死亡,后台线程也会自动死亡。 
  例子:JVM的垃圾回收线程。 
  调用Thread对象的setDaemon(true)方法可将指定线程设置成后台线程。isDaemon()方法,判断是否后台线程。 
  前台线程创建的子线程默认是前台线程,后台…..默认是….后台线程。

package thread;/**
 * Created by Zen9 on 2016/3/4.
 */public class DaemonThreadTest extends Thread{
    public DaemonThreadTest(String name){        super(name);
    }    @Override
    public void run() {        for (int i = 0; i < 1000; i++) {
            System.out.println(getName()+":"+i);
        }
    }    public static void main(String[] args) {
        DaemonThreadTest daemonThreadTest = new DaemonThreadTest("后台线程");        //设置为后台线程
        daemonThreadTest.setDaemon(true);
        daemonThreadTest.start();        for (int i = 0; i < 10; i++) {
            System.out.println(Thread.currentThread().getName()+":"+i);
        }
    }
}123456789101112131415161718192021222324252627

这里写图片描述

2.3 线程睡眠:sleep

  如果需要让当前正在执行的线程暂停一段时间,并进入阻塞状态,则可以通过调用Thread类的静态sleep()方法来实现。

package thread;import java.util.Date;/**
 * Created by Zen9 on 2016/3/4.
 */public class SleepMethodTest {
    public static void main(String[] args) throws Exception{        for (int i = 0; i < 10; i++) {
            System.out.println("当前时间:"+new Date());            if (i==7){                //参数为毫秒
                Thread.sleep(10000);
            }
        }
    }
}123456789101112131415161718

这里写图片描述

2.4 线程让步:yield

  yield():让当前正在执行的线程暂停,但不会阻塞该线程。只是将该线程转入就绪状态。yield()只是让当前线程暂停一下,让系统的线程调度器重新调度一次。 
  当某个线程调用了yield()方法暂停之后,只有优先级与当前线程相同或者优先级比较当前线程更高的处于就绪状态的线程才会获得执行的机会。

2.5 改变线程优先级

  每个线程执行时都具有一定的优先级,优先级高的线程获得较多的执行机会,而优先级低的线程则获得较少的执行机会。 
  每个线程默认的优先级都与创建它的父线程的优先级相同。 
  Thread类提供的方法:

  • setPriority(int newPriority):设置线程优先级,参数是一个整数,范围1~10,或使用三个静态常量:MAX_PRIORITY(10)、MIN_PRIORITY(1)、NORM_PRIORITY(5)

  • getPriority():获取线程优先级

推荐使用三个静态常量来设置线程优先级,这样才可以程序具有最好的移植性。因为不同的操作系统优先级不相同(有的为7个,有的为10个)。