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<Integer> products=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是一个随机数,目的是是的生产和消费的时间更随机不是每次都一样。