线程:指的是进程中一个单一顺序的控制流,一个进程可以并发多个线程,每条线程并行执行不同的任务。
进程:一个进程包括由操作系统分配的内存空间,包含一个或多个进程。一个线程不能独立存在,他必须是进程的一部分。一个线程一直运行,直到所有的非守护线程运行结束之后才能结束
线程的生命周期有五种状态:
- 新建状态
适应new关键字和Thread或者器子类建立一个线程对象之后,该线程处于新建状态,他会保持这个状态知道程序start这个线程
- 就绪状态
当线程对象调用了start()方法之后,该线程就进入就绪状态。就绪状态的线程处于就绪队列中,要等待JVM里线程调度器的调度。
- 运行状态
如果就绪状态的线程获取 CPU 资源,就可以执行 run(),此时线程便处于运行状态。处于运行状态的线程最为复杂,它可以变为阻塞状态、就绪状态和死亡状态。
- 阻塞状态
如果一个线程执行了sleep(睡眠)、suspend(挂起)等方法,失去所占用资源之后,该线程就从运行状态进入阻塞状态。在睡眠时间已到或获得设备资源后可以重新进入就绪状态。可以分为三种:
等待阻塞:运行状态中的线程执行 wait() 方法,使线程进入到等待阻塞状态。
同步阻塞:线程在获取 synchronized 同步锁失败(因为同步锁被其他线程占用)。
其他阻塞:通过调用线程的 sleep() 或 join() 发出了 I/O 请求时,线程就会进入到阻塞状态。当sleep() 状态超时,join() 等待线程终止或超时,或者 I/O 处理完毕,线程重新转入就绪状态。
- 死亡状态
线程执行完毕
创建线程的三种方式
一. 第一种方法就是继承Thread类,重写run()方法,然后调用Thread实例的start()方法启动线程
一旦线程启动后start方法就会立即返回,而不会等待到run方法执行完毕才返回。就好像run方法是在另外一个cpu上执行一样。当run方法执行后,将会打印出字符串new thread。
public class MyThread02 extends Thread{
public static void main(String[] args) {
Thread thread = new Thread(){
public void run() {
System.out.println("new thread");
}
};
thread.start();
}
}
二. 实现Runnable接口创建线程类
定义Runnable的实现类,重写run()方法。 创建Runnable实现类的实例,并以此作为Thread的target来创建对象,该对象才是真正的线程对象。
public class MyRunable implements Runnable {
@Override
public void run() {
// TODO Auto-generated method stub
System.out.println("new runable");
}
}
Thread mythread= new Thread(new MyRunable());
为了使线程能够执行run()方法,需要在Thread类的构造函数中传入 MyRunnable的实例对象。mythread.start();
三. 使用Callable和Future创建线程
public class Thread3 {
public static void main(String[] args) {
FutureTask<Integer> task = new FutureTask<Integer>((Callable<Integer>) () -> {
return 5;
});
new Thread(task, "有返回值的线程").start();
try {
System.out.println("子线程的返回值:" + task.get());
} catch (Exception e) {
e.printStackTrace();
}
}
}
三种方式创建线程的区别
- 继承Thread的方式不能在继承其他父类,而实现Runable接口和Callable接口可以继承其他父类
- 继承Thread方式实现简单,并使用this方法就可以可以访问到当前线程,通过实现接口的方式只能通过Thread.currentThread()方法。