概述
进程
- 是正在运行的程序
- 是系统进行资源分配和调用的独立单位
- 每一个进程都有它自己的内存空间和系统资源
线程
- 进程中的单个顺序控制流,是一条执行路径
- 单线程: 一个进程如果只有一条执行路径,则称为单线程程序
- 多线程: 一个进程如果有多条执行路径,则称为多线程程序
多线程
线程两种常见实现方式
-
方法一:将一个类声明为Thread的子类(继承), 这个子类应该重写Thread类的run方法 。 然后可以分配并启动子类的实例。
- 实现步骤
- 定义一个类 MyThread 继承 Thread 类
- 在 MyThread 类中 重写 run() 方法
- 创建 MyThread 类的对象
- 启动线程 start() 导致此线程开始执行,Java 虚拟机调用此线程的 run 方法
- 示例代码
class MyThread extends Thread{ @Override public void run() { System.out.println("你好!"); } } public class Test1 { public static void main(String[] args) { // 创建 MyThread 对象 MyThread mt = new MyThread(); // 启动线程 mt.start(); } }
运行结果:你好!
- 实现步骤
-
方法二:声明实现类 Runnable 接口(实现),那个类
- 实现步骤
- 定义一个类 MyRunnable 实现 Runnable 接口
- 在 MyRunnable 类中 重写 run() 方法
- 创建 MyRunnable 类的对象
- 创建 Thread 类的对象,把 MyRunnable 对象作为构造方法的参数
- 启动线程 start() 导致此线程开始执行,Java 虚拟机调用此线程的 run 方法
- 示例代码
class MyRunnable implements Runnable{ @Override public void run() { System.out.println("欢迎来到编程世界!"); } } public class Test2 { public static void main(String[] args) { // 创建 线程类对象 MyRunnable mr = new MyRunnable(); // 创建 Thread 对象 Thread t = new Thread(mr); // 启动线程 t.start(); } }
运行结果:欢迎来到编程世界!
- 实现步骤
-
问题?
- 为什么重写 run() 方法?
- run() 方法是用来 封装被线程执行的代码,也就是线程要做的任务
- run() 方法 和 start() 方法的区别?
- 直接调用 run() 方法,相当于 main 线程去执行 run() 方法,并无新的线程产生
- 调用 start() 方法,创建并启动线程,由 JVM 调用 run() 方法
- 为什么重写 run() 方法?
-
异常 IllegalThreadStateException
- 同一个线程对象,只能调用一次 start() 方法,不能两次调用,调用两次会,抛出 上述异常。
- 示例代码
class MyThread extends Thread{ @Override public void run() { System.out.println("你好!"); } } public class Test1 { public static void main(String[] args) { // 创建 MyThread 对象 MyThread mt = new MyThread(); // 启动线程 mt.start(); mt.start(); } }
- 异常演示
- 为什么会产生上述异常呢?我们来看下 start() 的源码
-
A zero status value corresponds to state “NEW” 意思是 零状态值对应于状态“NEW”
状态 描述 新建(NEW) 表示线程已经创建好了,但是还没有调用 start() 方法 就绪(RUNNABLE) 表示线程可能在运行,也可能在就绪队列 阻塞(BLOCKED) 表示线程处于等在锁的状态 等待(WAITING) 表示线程处于条件等待状态,当触发条件后会唤醒 计时等待(TIME_WAIT) 比 WAITING 多了个超时条件触发的机制 终止(TERMINATED) 表示线程执行结束 线程第二次调用 start() 方法时,可能处于终止或者其他非 NEW 状态,对线程判断是否处于 NEW 状态,不是,就会抛出异常
两种实现方式的好处?
- 相比继承 Thread 类,实现 Runnable 接口的好处?
- 避免了Java单继承的局限性
- 适合多个相同程序的代码去处理同一个资源的情况,把线程和程序的代码、数据有效分离,较好的体现了面向对象的设计思想
线程执行的随机性
-
线程执行的随机性,原因是线程"抢占式执行"的方式,谁先抢到,CPU分配资源,谁先执行的缘故。
-
示例代码
class MyThread extends Thread{ private int value; public MyThread(int value){ this.value = value; } @Override public void run() { System.out.println(value); } } public class Test1 { public static void main(String[] args) { // 创建 MyThread 对象 MyThread mt1 = new MyThread(1); MyThread mt2 = new MyThread(2); MyThread mt3 = new MyThread(3); MyThread mt4 = new MyThread(4); // 启动线程 mt1.start(); mt2.start(); mt3.start(); mt4.start(); } }
-
输出结果
注意:由于随机性,所以运行多次产生的结果是可能不一致的