线程创建的两种方式比较
继承Thread类:
class MyThread extends Thread{
......
@Override
public void run(){
......
}
}
MyThread mt=new MyThread();//创建线程
mt.start(); //启动线程
实现Runnable接口:
class MyThread implements Runnable{
......
@Override
public void run(){
......
}
}
MyThread mt=new MyThread();
Thread td=new Thread(mt);//创建线程
td.start(); //启动线程
从这两种方式可以看出,无论是哪种方式我们创建线程都要new出来一个thread类的对象,启动对象,要调用thread类的start方法。
两种方式比较:
1.Java中一个子类,可以实现多个接口,但是只能继承一个父类。所以Runnable方式可以避免Thread方式由于Java单继承特性带来的缺陷。
2.Runnable的代码可以被多个线程(Thread实例)共享,适合于多个线程处理同一资源的情况。(以下用代码模拟卖票举例说明。)
Thread&Runnable分别模拟卖火车票
我们模拟火车站3个窗口总共卖5张票的情况。
•Thread方式
class MyThread extends Thread{
private int ticketsCount=5; //一共有5张火车票
private String name; //窗口, 也即是线程的名字
public MyThread(String name){
this.name=name;
}
@Override
public void run(){
while(ticketsCount>0){
ticketsCount--; //如果还有票,就卖掉一张票
System.out.println(name+"卖掉了1张票,剩余票数为:"+ticketsCount);
}
}
}
public class TicketsThread{
public static void main(String args[]){
//创建三个线程,模拟三个窗口卖票
MyThread mt1=new MyThread("窗口1");
MyThread mt2=new MyThread("窗口2");
MyThread mt3=new MyThread("窗口3");
//启动三个线程,也即是窗口,开始卖票
mt1.start();
mt2.start();
mt3.start();
}
}
输出结果:
窗口3卖掉了1张票,剩余票数为:4
窗口3卖掉了1张票,剩余票数为:3
窗口3卖掉了1张票,剩余票数为:2
窗口1卖掉了1张票,剩余票数为:4
窗口1卖掉了1张票,剩余票数为:3
窗口1卖掉了1张票,剩余票数为:2
窗口1卖掉了1张票,剩余票数为:1
窗口1卖掉了1张票,剩余票数为:0
窗口2卖掉了1张票,剩余票数为:4
窗口3卖掉了1张票,剩余票数为:1
窗口2卖掉了1张票,剩余票数为:3
窗口2卖掉了1张票,剩余票数为:2
窗口3卖掉了1张票,剩余票数为:0
窗口2卖掉了1张票,剩余票数为:1
窗口2卖掉了1张票,剩余票数为:0
结果确是每个窗口卖了5张票,这个和我们期望的明显不一样。我们待会分析,先看Runnable接口下模拟卖票的情况。
•Runnable方式
class MyThread implements Runnable {
private int ticketsCount = 5; // 一共有5张火车票
public void run() {
while (ticketsCount > 0) {
ticketsCount--; // 如果还有票,就卖掉一张票
System.out.println(Thread.currentThread().getName()
+ "卖掉了1张票,剩余票数为:" + ticketsCount);
}
}
}
public class TicketsRunnable {
public static void main(String[] args) {
MyThread mt = new MyThread();
//创建三个线程来模拟三个售票窗口
Thread th1 = new Thread(mt, "窗口1");
Thread th2 = new Thread(mt, "窗口2");
Thread th3 = new Thread(mt, "窗口3");
//启动三个线程,也即是三个窗口,开始卖票
th1.start();
th2.start();
th3.start();
}
}
输出结果:
窗口1卖掉了1张票,剩余票数为:4
窗口2卖掉了1张票,剩余票数为:3
窗口2卖掉了1张票,剩余票数为:0
窗口3卖掉了1张票,剩余票数为:2
窗口1卖掉了1张票,剩余票数为:1
这里的输出结果是随机的,剩余票数并不是一定为43210的原因是,出现了资源的抢占。可以理解为,A窗口票已经卖掉了,然后票据正在打印。正在A窗口打印票据的时候,B窗口也卖了一张,并且打印的比A窗口快,先打印出票据。
分析:
Thread方式中,我们new了3个线程,每个线程都有自己的ticketsCount,3个ticketsCount是独立的,不是共享的,所以卖出了15张票。
线程的生命周期
•创建:新建一个线程对象,如Thread thd=new Thread()
•就绪:创建了线程对象后,调用了线程的start()方法(此时线程知识进入了线程队列,等待获取CPU服务 ,具备了运行的条件,但并不一定已经开始运行了)
•运行:处于就绪状态的线程,一旦获取了CPU资源,便进入到运行状态,开始执行run()方法里面的逻辑
•终止:线程的run()方法执行完毕,或者线程调用了stop()方法,线程便进入终止状态
•阻塞:一个正在执行的线程在某些情况系,由于某种原因而暂时让出了CPU资源,暂停了自己的执行,便进入了阻塞状态,如调用了sleep()方法