1、线程和进程
进程(Process)是计算机中的程序关于某数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位,是操作系统结构的基础。在早期面向进程设计的计算机结构中,进程是程序的基本执行实体;在当代面向线程设计的计算机结构中,进程是线程的容器。程序是指令、数据及其组织形式的描述,进程是程序的实体。所有运行中的任务通常就是一个进程,当一个程序进入内存运行时,即变成一个进程。进程是处于运行过程中的程序。
进程的三个特征:
- 对立性:进程是系统中独立存在的实体,它可以拥有自己独立的资源。每个进程都有自己私有的地址空间
- 动态性:程序是一个静态的指令集合,进程是一个正在系统中活动的指令集合。
- 并发性:多个进程可以在单个处理器上并发执行,多个进程之间互不影响。
2、线程的创建和启动
2.1、继承Thread类创建线程类
通过继承Thread类来创建并启动多线程的步骤
1、定义Thread的子类,并重写run()方法,该方法就是线程执行体。
2、创建Thread子类的实例
3、调用线程对象的start()方法来启动该线程,不论使用什么方式创建线程,启动都是调用start()方法
package usethread;
public class FirstThread extends Thread {
private int i;
//重写run()方法,run()方法的方法体就是线程执行体
public void run() {
for(; i < 10; i++) {
//当线程继承Thread类时,直接使用this即可获取当前线程
System.out.println(this.getName() +" " + i);
}
}
public static void main(String[] args) {
for(int i = 0; i < 10; i++) {
//调用Thread类的currentThread()方法获取当前线程
//这里的线程是main()方法的方法体,也就是线程执行体
System.out.println(Thread.currentThread().getName()+ " " + i);
if(i == 5) {
//创建并启动第一个线程
new FirstThread().start();
//创建并启动第二个线程
new FirstThread().start();
}
}
}
}
2.2、实现Runnable接口创建线程类
实现Runnable接口创建线程类
1、定义Runnable接口的实现类,并重写该接口的run()方法,该方法就是线程执行体
2、创建Runnable实现类的实例,并以此实例作为Thread的target来创建Thread对象,该对象才是真正的线程对象(Thread中的构造方法,详细请看api)
3、调用线程对象的start()方法来启动该线程
package usethread;
public class SecondThread implements Runnable {
private int i;
//重写run()方法,run()方法的方法体就是线程执行体
public void run() {
for(; i < 10; i++) {
//当线程继承Thread类时,直接使用this即可获取当前线程,不能使用this
System.out.println(Thread.currentThread().getName() +" " + i);
}
}
public static void main(String[] args) {
for(int i = 0; i < 10; i++) {
//这里的线程是main()方法的方法体,也就是线程执行体
System.out.println(Thread.currentThread().getName()+ " " + i);
if(i == 5) {
//实例化Runnable接口的实例
SecondThread st = new SecondThread();
//通过new(target,name)方法创建新进程
new Thread(st,"线程1").start();
//通过new(target,name)方法创建新进程
new Thread(st,"线程2").start();
}
}
}
}
2.3、使用Callable和Future创建线程类
略
3、线程的生命周期
在线程的生命周期中,它要经过新建、就绪、运行、阻塞和死亡5中状态。
3.1、新建和就绪状态
当使用new关键字创建了一个线程之后,该线程就处于新建状态,当线程对象调用了start()方法之后,该线程处于就绪状态,处于这个状态的线程并没有开始运行,只是表示该线程可以运行了,至于该线程 何时开始运行,取决于JVM里线程调度器的调度。
package usethread;
public class InvokeRun extends Thread {
private int i;
//重写run()方法,run()方法的方法体就是线程执行体
public void run() {
for(; i < 10; i++) {
//直接调用run()方法时,Thread的this.getName()返回的是该对象的名字,而不是线程的名字
//使用Thread.currentThread().getName()总是获取当前线程的名字
System.out.println(Thread.currentThread().getName() + " " + i);
System.out.println(this.getName());
}
}
public static void main(String[] args) {
for(int i = 0; i < 10; i++) {
//调用Thread的currentThread()方法获取当前线程
System.out.println(Thread.currentThread().getName() + " " + i);
if(i == 5) {
//直接调用run()方法
//系统会把线程对象当成普通对象,把run()方法当成普通方法
//下面两行代码不会启动两个线程,而是依次执行两个run()方法
new InvokeRun().run();
new InvokeRun().run();
}
}
}
}
3.2、运行和阻塞状态
如果处于就绪状态的线程获得了CPU,开始执行run()方法的线程执行体,则线程处于运行状态。一个线程不可能一直处于运行状态,线程在运行中需要被中断,目的是使其他进程获得执行的机会。
当发生如下情况时,线程将会进入阻塞状态
1、线程调用sleep()方法主动进图阻塞状态
2、线程调用了一个阻塞式IO方法
3、线程试图获得一个同步监听器,但该线程正被其他线程所持有
4、线程在等待某个通知
5、程序调用了线程的suspend()方法将该线程挂起,易产生死锁
当发生如下情况时,线程将会解除阻塞状态
1、调用sleep()方法的线程经过了指定的时间
2、线程调用的阻塞式IO方法已经返回
3、线程成功地获得了试图取得的同步监听器
4、线程正在等待某个通知时,其他进程发出了一个通知
5、处于挂起状态的线程被调用了resume()恢复方法
3.3、线程死亡
线程会以如下三种方式结束,结束后就处于死亡状态
1、run()或call()方法执行完成,线程正常结束
2、线程抛出一个未不活的Exception或Error
3、直接调用该线程的stop()方法结束线程,易导致死锁
package usethread;
public class StartDead extends Thread {
private int i;
//重写run()方法,run()方法的方法体就是线程执行体
public void run() {
for(; i < 10; i++) {
System.out.println(this.getName() + " " + i);
}
}
public static void main(String[] args) {
//创建线程对象
StartDead sd = new StartDead();
//新建状态的线程
System.out.println(sd.isAlive());
for(int i = 0; i < 10; i++) {
System.out.println(Thread.currentThread().getName() + " " + i);
if(i == 2) {
//启动线程
sd.start();
//判断启动后线程的isAlive()值,输出true
//当线程处于新建,死亡两种状态时,输出false
System.out.println(sd.isAlive());
}
}
}
}