java 多线程系列文章列表, 请查看目录: 《java 多线程学习笔记》
1. 实现Runnable接口
实现Runnable接口方式, 是一种非常常用的方式. 适用于无须监控异步线程结束时间, 无须捕获异步线程返回值情景
直接继承Thread类是创建异步线程最简单的方式, 但并不常用. 匿名内部类的方式可以考虑.
1.1 Runnable 方式特点
- 面向接口编程, 松耦合设计
- 在多线程模式下,可实现对象资源共享. 但是需要注意使用原子类变量AtomicXXX
- 不能独立运行, 需绑定在Thread实例上运行
- 主线程不能监控子线程何时结束, 也不能获取子线程返回结果
- 切记启动异步线程的方式是调用star()方法, 而非调用run()方法.
- 主线程不能捕获子线程的抛出的异常, 通常会在run()方法中包裹一个最大的try-catch,自行处理异常
1.2 使用步骤
- 创建Runnable 子类, 实现run()方法. 可采用内部类, Lambda表达式,匿名内部类等方式.
- 创建Thread实例,绑定Callable实例
- 通过Thread实例的start()方法启动线程
1.3 适用场景
主线程无须监控异步线程何时结束, 也无须获取子线程执行结果
2. 异步线程示例
2.1 异步线程
public static void main(String[] args) {
// 创建runnable 实例
Runnable runnable = new Runnable() {
@Override
public void run() {
System.out.println("执行线程名称:" + Thread.currentThread().getName());
}
};
// 启动子线程
new Thread(runnable).start();
// 主线程输出
System.out.println("执行线程名:" + Thread.currentThread().getName());
}
2.2 输出
执行线程名称:Thread-0
执行线程名:main
3. 多线程示例
3.1 多线程示例
- 通过for循环创建多个线程只是为了测试, 企业开发中通常会使用线程池.
- 共享变量times需要使用原子类型变量, 否则会有并发问题
- 需要注意while判断条件的写法, 将获取, 自增, 判断作为一个原子操作进行, 否则也会有原子操作问题.
- 通常会将run方法整个方法体至于一个try-catch中, 当以后异常发生时自行捕获处理, 因为主线程不能捕获子线程中的异常.
public static void main(String[] args) {
try {
// 创建runnable 实例
Runnable runnable = new Runnable() {
// 记录总次数
private AtomicInteger times = new AtomicInteger(0);
@Override
public void run() {
// 当前线程执行第几次
int idx = 0;
while ((idx = times.getAndIncrement()) < 10) {
try {
Thread.sleep(200l);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " 执行第[" + idx + "]次操作");
}
}
};
// 启动子线程
for (int i = 0; i < 3; i++) {
new Thread(runnable).start();
}
// 主线程输出
System.out.println("执行线程名:" + Thread.currentThread().getName());
} catch (Exception e) {
e.printStackTrace();
}
}
3.2 输出
测试输出可以看出, 由3个线程共同执行了10次操作.
执行线程名:main
Thread-0 执行第[0]次操作
Thread-1 执行第[1]次操作
Thread-2 执行第[2]次操作
Thread-0 执行第[3]次操作
Thread-1 执行第[4]次操作
Thread-2 执行第[5]次操作
Thread-0 执行第[6]次操作
Thread-1 执行第[7]次操作
Thread-2 执行第[8]次操作
Thread-0 执行第[9]次操作