目录
一、线程简介
1、进程、线程
- 程序:开发写的代码称之为程序。程序就是一堆代码,一组数据和指令集,是一个静态的概念。
- 进程(Process):将程序运行起来,我们称之为进程。进程是执行程序的一次执行过程,它是动态的概念。进程存在生命周期,也就是说程序随着程序的终止而销毁。进程之间是通过TCP/IP端口实现交互的。
- 线程(Thread):线程是进程中的实际运作的单位,是进程的一条流水线,是程序的实际执行者,是最小的执行单位。通常在一个进程中可以包含若干个线程,当然一个进程中至少有一个线程。线程是CPU调度和执行的最小单位。
注意:很多多线程都是模拟出来的,真正的多线程是指有多个CPU,即多核,如服务器,如果是模拟出来的多线程,即一个CPU的情况下,在同一个时间点,CPU只能执行一个代码,因为切换的很快,所以就有同时执行的错觉。
2、并发、并行、串行
- 并发:同一个对象被多个线程同时操作。(这是一种假并行。即一个CPU的情况下,在同一个时间点,CPU只能执行一个代码,因为切换的很快,所以就有同时执行的错觉)。
- 并行:多个任务同时进行。并行必须有多核才能实现,否则只能是并发。
- 串行:一个程序处理完当前进程,按照顺序接着处理下一个进程,一个接着一个进行。
3、进程的三态
进程在运行的过程中不断的改变其运行状态。通常一个运行的进程必须有三个状态,就绪态、运行态、阻塞态。
- 就绪态:当进程获取出CPU外所有的资源后,只要再获得CPU就能执行程序,这时的状态叫做就绪态。在一个系统中处于就绪态的进程会有多个,通常把这些排成一个队列,这个就叫就绪队列。
- 运行态:当进程已获得CPU操作权限,正在运行,这个时间就是运行态。在单核系统中,同一个时间只能有一个运行态,多核系统中,会有多个运行态。
- 阻塞态:正在执行的进程,等待某个事件而无法继续运行时,便被系统剥夺了CPU的操作权限,这时就是阻塞态。引起阻塞的原因有很多,比如:等待I/O操作、被更高的优先级的进程剥夺了CPU权限等。
二、线程实现
1、继承Thread类
步骤:
- 自定义线程类继承Thread类
- 重写run()方法,编写线程执行体
- 创建线程对象,调用start()方法启动线程(启动后不一定立即执行,抢到CPU资源才能执行)
代码如下(示例):
// 自定义线程对象,继承Thread,重写run()方法
public class MyThread extends Thread {
public MyThread(String name){
super(name);
}
@Override
public void run() {
// 线程执行体
for (int i = 0; i < 10; i++) {
System.out.println("我是自定义" + Thread.currentThread().getName() + "--" + i);
}
}
public static void main(String[] args) {
// main线程,主线程
// 创建线程实现类对象
MyThread thread = new MyThread("线程1");
MyThread thread2 = new MyThread("线程2");
// 调用start()方法启动线程
thread.start();
thread2.start();
for (int i = 0; i < 10; i++) {
System.out.println("我是主线程--" + i);
}
}
}
执行结果:
我是自定义线程2--0
我是自定义线程2--1
我是主线程--0
我是自定义线程1--0
我是主线程--1
我是主线程--2
我是自定义线程2--2
我是主线程--3
我是自定义线程1--1
我是主线程--4
我是主线程--5
我是主线程--6
我是主线程--7
我是主线程--8
我是主线程--9
我是自定义线程2--3
我是自定义线程1--2
我是自定义线程2--4
我是自定义线程1--3
我是自定义线程1--4
我是自定义线程1--5
我是自定义线程1--6
我是自定义线程1--7
我是自定义线程1--8
我是自定义线程1--9
我是自定义线程2--5
我是自定义线程2--6
我是自定义线程2--7
我是自定义线程2--8
我是自定义线程2--9
2、实现Runnable接口
步骤:
- 自定义线程类实现Runnable接口
- 实现run()方法,编写线程体
- 创建线程对象,调用start()方法启动线程(启动后不一定立即执行,抢到CPU资源才能执行)
代码如下(示例):
// 自定义线程对象,实现Runnable接口,重写run()方法
public class MyRunnable implements Runnable {
@Override
public void run() {
// 线程执行体
for (int i = 0; i < 10; i++) {
System.out.println("我是自定义" + Thread.currentThread().getName() + "--" + i);
}
}
public static void main(String[] args) {
// main线程,主线程
// 创建实现类对象
MyRunnable myRunnable = new MyRunnable();
// 创建代理类对象
Thread thread = new Thread(myRunnable,"线程1");
Thread thread2 = new Thread(myRunnable,"线程2");
// 调用start()方法启动线程
thread.start();
thread2.start();
for (int i = 0; i < 10; i++) {
System.out.println("我是主线程--" + i);
}
}
}
执行结果:
我是主线程--0
我是自定义线程1--0
我是自定义线程2--0
我是自定义线程1--1
我是主线程--1
我是自定义线程1--2
我是自定义线程2--1
我是自定义线程1--3
我是主线程--2
我是主线程--3
我是自定义线程1--4
我是自定义线程2--2
我是自定义线程2--3
我是自定义线程2--4
我是自定义线程1--5
我是自定义线程1--6
我是主线程--4
我是自定义线程1--7
我是自定义线程1--8
我是自定义线程1--9
我是自定义线程2--5
我是自定义线程2--6
我是自定义线程2--7
我是自定义线程2--8
我是主线程--5
我是自定义线程2--9
我是主线程--6
我是主线程--7
我是主线程--8
我是主线程--9
3、实现Callable接口(不常用)
步骤:
- 实现Callable接口,先要返回值类型
- 重写call()方法,需要抛出异常
- 创建目标对象
- 创建执行服务:ExecutorService ser = Executor.newFixedThreadPool(1);
- 提交执行:Future<Boolean> res = ser.submit(t1);
- 获取结果:boolean r1 = res.get();
- 关闭服务:ser.shutdownNow();
代码如下(示例):
import java.util.concurrent.*;
// 自定义线程对象,实现Callable接口,重写call()方法
public class MyThread implements Callable<Boolean> {
@Override
public Boolean call() throws Exception {
// 线程执行体
for (int i = 0; i < 10; i++) {
System.out.println("我是自定义" + Thread.currentThread().getName() + "--" + i);
}
return true;
}
public static void main(String[] args) throws ExecutionException,
InterruptedException {
// main线程,主线程
// 创建线程实现类对象
MyThread thread = new MyThread();
MyThread thread2 = new MyThread();
// 创建执行服务,参数是线程池线程数量
ExecutorService ser = Executors.newFixedThreadPool(2);
// 提交执行
Future<Boolean> res = ser.submit(thread);
Future<Boolean> res2 = ser.submit(thread2);
// 获取结果
boolean r1 = res.get();
boolean r2 = res2.get();
// 关闭服务
ser.shutdownNow();
}
}
执行结果:
我是自定义pool-1-thread-1--0
我是自定义pool-1-thread-2--0
我是自定义pool-1-thread-1--1
我是自定义pool-1-thread-1--2
我是自定义pool-1-thread-1--3
我是自定义pool-1-thread-1--4
我是自定义pool-1-thread-1--5
我是自定义pool-1-thread-2--1
我是自定义pool-1-thread-1--6
我是自定义pool-1-thread-2--2
我是自定义pool-1-thread-2--3
我是自定义pool-1-thread-2--4
我是自定义pool-1-thread-2--5
我是自定义pool-1-thread-2--6
我是自定义pool-1-thread-2--7
我是自定义pool-1-thread-2--8
我是自定义pool-1-thread-2--9
我是自定义pool-1-thread-1--7
我是自定义pool-1-thread-1--8
我是自定义pool-1-thread-1--9
三、线程常用方法
1、线程的状态
- 新建状态(NEW):线程已创建,尚未调用start()方法启动之前。
- 运行状态(RUNNABLE):线程对象被创建后,调用该对象的start()方法,并获取CPU权限进行执行。
- 阻塞状态(BLOCKED):线程在获取synchronized同步锁失败(因为锁被其它线程所占用),它会进入同步阻塞状态。
- 等待状态(WAITING ):等待状态。正在等待另一个线程执行特定动作来唤醒该线程的状态。
- 超时等待状态(TIME_WAITING):有明确结束时