15.4 线程的状态

1.新创建(new)。就是线程刚刚创建,又没有启动的时候。
2.可运行(runnable)。线程可运行的状态。
3.被阻塞(blocked)。当线程试图获取资源的时候,又无法得到,此时线程处于被阻塞的状态。
4.等待(waiting)。主动让线程等待,Object.wait方法,Thread.join方法,Thread.sleep,或是等待java.util.concurrent.Lock或者Condition时,线程就会处于这个状态。
5.计时等待(time waiting)。有些让线程等待的方法有一个超时参数,例如sleep,超时后线程会被调度为可运行状态。
6.终止(terminated)。线程的run方法运行完毕,或者run里面出现了异常又没有捕获,都会到这这个线程被终止。

可由线程的getState()方法获得线程当前的状态。
我们通常需要关心的是如何让线程等待或者阻塞,以及唤醒。
最简单的一个等待就是Thread.sleep方法,它接受一个毫秒为单位的参数,例如这里第4行代码,等待1毫秒后自动唤醒,这个方法需要处理InterruptedException异常:
public void run(){
    System.out.println(i);
    try{
        Thread.sleep(1);
    }
    catch(InterruptedException e){
        e.printStackTrace();
    }
    i++;
}
这是计时等待,它会自动唤醒。
Object类也有一个等待wait,这是个非静态方法,有计时和非计时,如果是非计时,那么就需要用notify或者notifyAll唤醒了,例如线程A因为某个条件尚未成立等待了,然后B线程使得这个条件成立了,调用notify唤醒队列中的一个线程,或者用notifyAll唤醒队列中所有的线程。
下面是一个生产/消费的多线程例子,若干个线程生产,生产后放入队列;若干个线程从产品队列中取一个产品消费,如果产品队列是空的,不消费,阻塞。如果产品队列放入了产品,马上通知所有消费线程(也就是notifyAll):
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
public class ProductTest{
    private long SLEEP=2000;
    private static List<Integerproducts=Collections.synchronizedList(new LinkedList());
    public void produce(){
        synchronized(products){
            int random=(int)(Math.random()*100);
            try{
                Thread.sleep(SLEEP+random);
            }
            catch(InterruptedException e){
                e.printStackTrace();
            }
            products.add(random);
            System.out.println("新产品上线!");
            products.notifyAll();
            System.out.println("工厂生产产品"+random);
        }
    }
    public void consume(){
        synchronized(products){
            while(products.isEmpty()){
                try{
                    System.out.println("没有产品,等待");
                    products.wait();
                }
                catch(InterruptedException e){
                    e.printStackTrace();
                }
            }
            int t=products.remove(0);
            int random=(int)(Math.random()*100);
            try{
                Thread.sleep(SLEEP+random);
            }
            catch(InterruptedException e){
                e.printStackTrace();
            }
            System.out.println("消费者消耗产品"+t);
        }
    }
    public static void main(String[]args)throws InterruptedException{
        ProductTest f=new ProductTest();
        Runnable use=()-{
            f.consume();
        };
        Runnable build=()-{
            f.produce();
        };
        while(true){
            Thread t1=new Thread(build);
            Thread t11=new Thread(use);
            Thread t2=new Thread(build);
            Thread t21=new Thread(use);
            t11.start();
            t1.start();
            t2.start();
            t21.start();
        }
    }
}
这个例子有两个生产者和两个消费者,SLEEP参数是休眠时间,目的是为了输出不要太快。此程序是死循环不能自行结束,我们可以在控制台看到生产、消费的交替过程,有时候还会因为没有产品而阻塞。random是一个随机数,目的是是的生产和消费的时间更随机不是每次都一样。