进程 & 线程的区别、实现多线程、线程的生命周期、模拟售票系统
进程:是系统进行资源分配和调度的一个独立单位。
1、独立性,进程是系统中独立存在的实体,它可以拥有自己的独立资源,每一个进程都有自己的私有地址空间。在没有进过进程本身允许的情况下,一个用户进程不可以直接访问其他进程的地址空间。
2、动态性,进程与程序的区别在于,程序只是一个静态的指令集合,而进程是一个正在系统中活动的指令集合。进程具有自己的生命周期和各种不同的状态。
3、并发性,多个进程可以在单个处理器上并发执行,多个进程之间不会互相影响。
线程:也被称作轻量级进程,线程是进程的执行单元,一个进程可以有多个线程。线程不拥有资源,它与父进程的其它线程共享该进程所拥有的资源。线程的执行时抢占式的。
在java中一个类要当做线程类使用有两种方法;
1、继承Thread类,并重写run函数
2、 实现Runnable接口,并重写run函数
通过继承Thread类来创建并启动多线程的步骤如下:
1、定义Thread类的子类,并重写该类的run方法,run方法的方法体代表线程需要完成的任务。
2、创建Thread子类的实例
3、用线程对象的start方法来启动该线程。
public class Test2 {
public static void main(String[] args) {
for (int i = 0; i <100; i++) {
System.out.println(Thread.currentThread().getName()+" "+i);
if(i==20){
Cat c1=new Cat();
Cat c2=new Cat();
c1.start();
c2.start();}} }}
class Cat extends Thread {
private int i;
public void run(){
for (; i < 100; i++) {
System.out.println(getName()+" "+i); }}}
结果如图:
虽然上面程序只显式的创建并启动了两条线程,但实际上程序至少有3条线程:程序显式创建的2个子线程和主线程。因为当java程序开始运行后,程序至少会创建一条主线程,主线程的执行体不是由run方法来确定的,而是由main方法来确定,main方法的方法体代表主线程的执行体。
提示:1、Runnable对象仅仅作为Thread对象的target,Runnable实现类里面的run方法仅作为线程执行体,而实际的线程对象依然是Thread实例。
2、采用Runnable接口的方式类创建的多条线程可以共享线程类的实例属性,因为在这种方式下,程序锁创建的Runnable对象只是线程的target,而多条线程可以共享一个target,所以多条线程可以共享同一个线程类的实例属性。
对于Thread来说,Runnable有以下特点;
1、线程类只是实现了Runnable接口,还可以继承其他类。
2、在这种方式下,可以多个线程共享一个target对象,so适合多个相同线程来处理同一份资源的情况。
3、编程稍稍复杂,如要访问当前线程,必须使用Thread.currentThread()方法。
线程的生命周期:包括新建(new)、就绪(Runnable)、运行(Running)、阻塞(Blocked)和死亡(Dead)五种状态。
当程序使用new关键字创建了一个线程后,该线程就处于新建状态。当线程对象调用了start()方法后,该线程处于就绪状态。当处于就绪的线程获得CPU,开始执行run方法,则该线程处于运行状态。
线程会以以下三种方式之一结束,结束后就处于死亡状态:
1、run()方法执行完成,线程正常结束。
2、线程抛出一个未捕获的Exception或Error
3、直接调用该线程的stop()方法来结束该线程--该方法容易导致死锁,不推荐使用。
注意:启动线程不要用run方法。调用start来启动线程,系统会把该run方法当成执行体来处理,如果直接调用run方法,则run方法会立即被执行,而且在run方法返回之前其他线程无法并发执行。在线程已死亡的情况下再次调用start()方法,会引发IllegalThreadStateException异常;对于新建状态的线程两次调用start()方法也是错误的。
//售票模拟
public class Demo5 {
public static void main(String[] args) {
// 定义售票窗口
TicketWindow tw1=new TicketWindow();
Thread t1=new Thread(tw1);
Thread t2=new Thread(tw1);
Thread t3=new Thread(tw1);
t1.start();
t2.start();
t3.start();}}
class TicketWindow implements Runnable{
private int nums=2000;//票数
public void run() {
while(true){
//认为if else要保证原子性
synchronized(this){//同步代码块
if(nums>0){//先判断是否还有票
System.out.println(Thread.currentThread().getName()+"正在售出第"+nums+"张票");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
nums--;
}else{
//售票结束
break;
}}}}}