进程:每个进程都有独立的代码和数据空间(进程上下文),进程间的切换会有较大的开销,一个进程包含1--n个线程。
线程:同一类线程共享代码和数据空间,每个线程有独立的运行栈和程序计数器(PC),线程切换开销小。
线程和进程一样分为五个阶段:创建、就绪、运行、阻塞、终止。
多进程是指操作系统能同时运行多个任务(程序)。
多线程是指在同一程序中有多个顺序流在执行
在java中要想实现多线程,有两种手段,一种是继续Thread类,另外一种是实现Runable接口。
Thread类是在java.lang包中定义的。一个类只要继承了Thread类同时覆写了本类中的run()方法就可以实现多线程操作了,但是一个类只能继承一个父类,这是此方法的局限。
一、多线程建立方式
Thread&Runnable分别模拟卖火车票
1、thread方式
class MyThread extends Thread { private int ticketsCont=5; //一共有5张火车票 private String name; //窗口, 也即是线程的名字 public MyThread(String name){ this.name=name; } @Override public void run(){ while(ticketsCont>0){ ticketsCont--; //如果还有票,就卖掉一张票 System.out.println(name+"卖掉了1张票,剩余票数为:"+ticketsCont); } } } 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(); } }
这样程序可以正常完成交互式运行。那么为啥非要使用start();方法启动多线程呢?
在JDK的安装路径下,src.zip是全部的java源程序,通过此代码找到Thread中的start()方法的定义,可以发现此方法中使用了private native void start0();其中native关键字表示可以调用操作系统的底层函数,那么这样的技术成为JNI技术(java Native Interface)。
2、Runnable方式
步骤:
1,定义类实现Runnable接口
2,覆盖Runnable接口中的run方法
将线程要运行的代码存放在该run方法中
3,通过Thread 类建立线程对象
4,将Runnable接口的子类对象作为实际参数传递给Thread类的构造函数
为什么要将Runnable接口中的子类对象传递给Thread的构造函数
因为,自定义的run方法所属的对象是Runnable接口的子类对象
所以要让线程去指定指定对象的run方法,就必须明确该run方法所属对象。
5,调用Thread类的start方法 开启线程并调用Runnable接口子类的run方法
class MyThread2 implements Runnable { private int ticketsCont=5; //一共有5张火车票 @Override public void run(){ while(true){ synchronized(this){ if(ticketsCont<=0){ break; } ticketsCont--; //如果还有票,就卖掉一张票 System.out.println(Thread.currentThread().getName()+"卖掉了1张票,剩余票数为:"+ticketsCont); /*try{ Thread.sleep(50); //通过阻塞程序来查看效果 } catch(Exception e){ System.out.println(e); }*/ } } } } public class TicketsRunnable { public static void main(String args[]){ MyThread2 mt=new MyThread2(); //创建三个线程来模拟三个售票窗口 Thread th1=new Thread(mt,"窗口1"); Thread th2=new Thread(mt,"窗口2"); Thread th3=new Thread(mt,"窗口3"); //启动三个线程,也即是三个窗口,开始卖票 th1.start(); th2.start(); th3.start(); } }
但是在使用Runnable定义的子类中没有start()方法,只有Thread类中才有。此时观察Thread类,有一个构造方法:public Thread(Runnable targer)此构造方法接受Runnable的子类实例,也就是说可以通过Thread类来启动Runnable实现的多线程。(start()可以协调系统的资源):
二、总结
- 创建:新建一个线程对象,如Thread thd=new Thread()
- 就绪:创建了线程对象后,调用了线程的start()方法(此时线程知识进入了线程队列,等待获取CPU服务 ,具备了运行的条件,但并不一定已经开始运行了)
- 运行:处于就绪状态的线程,一旦获取了CPU资源,便进入到运行状态,开始执行run()方法里面的逻辑
- 终止:线程的run()方法执行完毕,或者线程调用了stop()方法,线程便进入终止状态
- 阻塞:一个正在执行的线程在某些情况系,由于某种原因而暂时让出了CPU资源,暂停了自己的执行,便进入了阻塞状态,如调用了sleep()方法
在基于线程实现的多线程上(例子1),则总共开辟了三个窗口,在每个窗口卖了5张票,总共卖了15张票,无法实现资源共享,造成程序出错。
在基于Runnable是,利用同步共享,实现资源共享,则三个窗口总共卖了5张票。
建议使用Runnable这种方式创建线程。 程序中的同一资源指的是同一个Runnable对象。安全的卖票程序中需要加入同步synchronized。