目录
0.知识拓扑
1. 多线程相关概念
1.1 进程
1.2 线程
1.3 java 中的进程 与 线程概述
1.4 CPU、进程 与 线程的关系
2.多线程的创建方式
2.1 继承Thread类
2.2 实现Runnable接口
2.3 实现Callable接口
2.4 三种创建方式对比
3.线程同步
3.1 线程同步机制概述
3.2 线程安全的产生以及解决方式
4.线程池
4.1 概述
(1)JUC
(2)JUC支持的线程池种类
(3)ThreadPool线程池特点
4.2 线程池创建的4中方式
(1)创建定长线程池
(2)创建缓存线程池
(3)创建单线程线程池
(4)可调度线程池
5.小结
0.知识拓扑
- 了解多线程序的概念
- 创建多线程的三种方式
- 初识线程同步
- 线程安全问题与解决方式
- 初始线程池及其应用
1. 多线程相关概念
1.1 进程
1.2 线程
1.3 java 中的进程 与 线程概述
进程 | 线程 | |
概述 | 进程是操作系统资源分配的基本单位 | 线程是CPU调度的基本单位,是进程内的执行单元 |
资源占用 | 拥有独立的内存空间(堆、栈、方法区等),创建和销毁开销较大 | 共享进程的内存资源,创建和切换开销小 |
通信方式 | 进程间通信(IPC)较复杂(如管道、套接字、共享内存等) | 通信更简单(共享内存) |
实现方式 | Runtime.exec() 或ProcessBuilder 创建新的进程 |
|
线程安全 | 由于线程共享内存,需要考虑同步问题:
| |
线程池 | Java提供了
|
1.4 CPU、进程 与 线程的关系
2.多线程的创建方式
2.1 继承Thread类
/*
需求: 通过继承Thread的方式 来创建三个线程,
--> 模拟参赛者董老、 参赛者yang菜鸡的10秒短跑程序
*/
code
public class ExtendsThreadDemo {
/*
需求: 通过继承Thread的方式 来创建三个线程,
--> 模拟参赛者董老、 参赛者yang菜鸡的10秒短跑程序
*/
class CreateThreadRunnner extends Thread {
public int speed = 2;
public CreateThreadRunnner(int speed){
this.speed = speed;
}
@Override
public void run() {
int x = 0;
for (int i = 0; i < 10; i++) {
Integer speed = new Random().nextInt(this.speed);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
x = x +i * speed;
System.out.println("第"+ i + "s:" + this.getName() + "跑了" +
x + "m【" + speed + "m/s】");
}
}
}
public void start() {
CreateThreadRunnner createThreadRunnnerA = new CreateThreadRunnner(666);
createThreadRunnnerA.setName("懂佬");
CreateThreadRunnner createThreadRunnerB = new CreateThreadRunnner(9);
createThreadRunnerB.setName("yang菜鸡");
createThreadRunnnerA.start();
createThreadRunnerB.start();
}
public static void main(String[] args) {
new ExtendsThreadDemo().start();
/**
* 第0s:yang菜鸡跑了0m【6m/s】
* 第0s:懂佬跑了0m【549m/s】
* 第1s:懂佬跑了1m【1m/s】
* 第1s:yang菜鸡跑了5m【5m/s】
* 第2s:懂佬跑了623m【311m/s】
* 第2s:yang菜鸡跑了13m【4m/s】
* 第3s:懂佬跑了1121m【166m/s】
* 第3s:yang菜鸡跑了13m【0m/s】
* 第4s:懂佬跑了1565m【111m/s】
* 第4s:yang菜鸡跑了25m【3m/s】
* 第5s:yang菜鸡跑了50m【5m/s】
* 第5s:懂佬跑了3290m【345m/s】
* 第6s:yang菜鸡跑了74m【4m/s】
* 第6s:懂佬跑了6200m【485m/s】
* 第7s:懂佬跑了10267m【581m/s】
* 第7s:yang菜鸡跑了130m【8m/s】
* 第8s:yang菜鸡跑了170m【5m/s】
* 第8s:懂佬跑了10819m【69m/s】
* 第9s:yang菜鸡跑了206m【4m/s】
* 第9s:懂佬跑了13195m【264m/s】
*/
}
}
2.2 实现Runnable接口
/* 需求: 通过实现Runnable接口的方式 来创建三个线程, --> 模拟参赛者索尼克、夏特、小红的5秒短跑程序 */
public class ImplementsRunnableDemo {
/*
需求: 通过实现Runnable接口的方式 来创建三个线程,
--> 模拟参赛者索尼克、夏特、小红的5秒短跑程序
*/
class CreateRunner implements Runnable{
public int speed;
public CreateRunner(int speed) {
this.speed = speed;
}
@Override
public void run() {
int sum = 0;
for(int k = 0; k < 5; k++){
Integer speed = new Random().nextInt(this.speed);
try{
Thread.sleep(500);
}catch(InterruptedException e){
e.printStackTrace();
}
sum += k * speed;
System.out.println("第"+ k + "s:" + Thread.currentThread().getName() + "跑了" +
sum + "m【" + speed + "m/s】");
}
}
}
public void start() {
CreateRunner runner1 = new CreateRunner(999);
Thread thread1 = new Thread(runner1);
thread1.setName("刺猬索尼克");
thread1.start();
Thread thread2 = new Thread(new CreateRunner(888));
thread2.setName("夏特");
thread2.start();
Thread thread3 = new Thread(new CreateRunner(788));
thread3.setName("小红");
thread3.start();
}
public static void main(String[] args) {
ImplementsRunnableDemo demo = new ImplementsRunnableDemo();
demo.start();
/**
* C:\Users\iayan\.jdks\corretto-1.8.0_432\bin\java.exe "-
* 第0s:小红跑了0m【287m/s】
* 第0s:夏特跑了0m【805m/s】
* 第0s:刺猬索尼克跑了0m【21m/s】
* 第1s:夏特跑了130m【130m/s】
* 第1s:小红跑了445m【445m/s】
* 第1s:刺猬索尼克跑了876m【876m/s】
* 第2s:夏特跑了944m【407m/s】
* 第2s:刺猬索尼克跑了2554m【839m/s】
* 第2s:小红跑了1519m【537m/s】
* 第3s:刺猬索尼克跑了3067m【171m/s】
* 第3s:小红跑了3325m【602m/s】
* 第3s:夏特跑了1976m【344m/s】
* 第4s:刺猬索尼克跑了6235m【792m/s】
* 第4s:小红跑了5177m【463m/s】
* 第4s:夏特跑了5328m【838m/s】
*/
}
}
2.3 实现Callable接口
允许线程创建后将值返回
public class ImplementsCallableDemo {
/*
需求: 通过实现Runnable接口的方式 来创建三个线程,
--> 模拟参赛者索尼克、夏特、小红的5秒短跑程序
*/
class CreateCallable implements Callable<Integer> {
public int speed;
public String name;
public CreateCallable(int speed) {
this.speed = speed;
}
@Override
public Integer call() throws Exception {
Integer speed = new Random().nextInt(this.speed);
Integer sumLength = 0;
for (int j = 1; j <= 6; j++) {
Thread.sleep(2000);
sumLength += j * speed;
System.out.println();
System.out.println("第"+ j + "s:" + this.name + "跑了" +
sumLength + "m【" + speed + "m/s】");
}
return sumLength;
}
}
public void start() throws ExecutionException, InterruptedException {
// 创建线程池
ExecutorService executorService = Executors.newFixedThreadPool(3);
CreateCallable thread1 = new CreateCallable(95);
thread1.name = "索尼克";
CreateCallable thread2 = new CreateCallable(97);
thread2.name = "夏特";
CreateCallable thread3 = new CreateCallable(92);
thread3.name = "大红";
Future<Integer> r1 = executorService.submit(thread1);
Future<Integer> r2 = executorService.submit(thread2);
Future<Integer> r3 = executorService.submit(thread3);
// 关闭线程池
executorService.shutdown();
System.out.println(thread1.name + "累计跑了:" + r1.get() + "m");
System.out.println(thread2.name + "累计跑了:" + r2.get() + "m");
System.out.println(thread3.name + "累计跑了:" + r3.get() + "m");
}
public static void main(String[] args) throws ExecutionException, InterruptedException {
new ImplementsCallableDemo().start();
/**
*
第1s:索尼克跑了46m【46m/s】
第1s:大红跑了79m【79m/s】
第1s:夏特跑了71m【71m/s】
第2s:大红跑了237m【79m/s】
第2s:夏特跑了213m【71m/s】
第2s:索尼克跑了138m【46m/s】
第3s:索尼克跑了276m【46m/s】
第3s:大红跑了474m【79m/s】
第3s:夏特跑了426m【71m/s】
第4s:夏特跑了710m【71m/s】
第4s:索尼克跑了460m【46m/s】
第4s:大红跑了790m【79m/s】
第5s:大红跑了1185m【79m/s】
第5s:夏特跑了1065m【71m/s】
第5s:索尼克跑了690m【46m/s】
第6s:大红跑了1659m【79m/s】
第6s:索尼克跑了966m【46m/s】
第6s:夏特跑了1491m【71m/s】
索尼克累计跑了:966m
夏特累计跑了:1491m
大红累计跑了:1659m
*/
}
}
2.4 三种创建方式对比
3.线程同步
3.1 线程同步机制概述
线程同步的三种方式:
/** * 线程同步的三种方式 * 1. synchronized代码块, 需要指定一个自定义对象作为所对象 * 2. synchronized方法, 通过this对象决定哪个对象拥有执行的权力 * 3. synchronized静态方法, Printer.class 进行加锁 */
/** * 需求: * 创建5个打印线程, * 有序的打印“不可逆转 * 不可逆转 * 不可逆转 * 不可逆转 * 不可逆转” */
code:
public class SyncLockDemo {
/**
* 需求:
* 创建5个打印线程,
* 有序的打印“不可逆转
* 不可逆转
* 不可逆转
* 不可逆转
* 不可逆转”
*/
/**
* 线程同步的三种方式
* 1. synchronized代码块, 需要指定一个自定义对象作为所对象
* 2. synchronized方法, 通过this对象决定哪个对象拥有执行的权力
* 3. synchronized静态方法, Printer.class 进行加锁
*/
class Printer{
Object printLock = new Object();
/**
*
*/
public void print(){
synchronized(printLock){
try {
Thread.sleep(300);
System.out.print("不");
Thread.sleep(300);
System.out.print("可");
Thread.sleep(300);
System.out.print("逆");
Thread.sleep(300);
System.out.print("转");
System.out.println();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
// 02 synchronized方法
public synchronized void print2(){
try {
Thread.sleep(300);
System.out.print("不");
Thread.sleep(300);
System.out.print("可");
Thread.sleep(300);
System.out.print("逆");
Thread.sleep(300);
System.out.print("转");
System.out.println();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
class PrintTask implements Runnable{
public Printer printer;
@Override
public void run() {
// printer.print();
printer.print2();
}
}
// 00- 单线程实现
// public void startPrint(){
// Printer printer = new Printer();
// printer.print();
// }
// 01 -多线程实现
public void startPrint(){
Printer printer = new Printer();
for(int k = 0; k < 5; k++){
PrintTask printTask = new PrintTask();
printTask.printer = printer;
new Thread(printTask).start();
}
}
public static void main(String[] args) {
SyncLockDemo demo = new SyncLockDemo();
demo.startPrint();
/** 0.未加同步锁
*
* 不不不不不可可可可可逆逆逆逆逆转
* 转
* 转
* 转
* 转
*/
/** 1.加上同步锁
* 不可逆转
* 不可逆转
* 不可逆转
* 不可逆转
* 不可逆转
*/
}
}
3.2 线程安全的产生以及解决方式
线程安全解决超卖问题:
(1)商品库存类
// 商品库存类
public class Stock {
// 当前商品库存剩余量 --》 3个
public static int count = 3;
}
(2)消费者类
// 商品库存类
public class Stock {
// 当前商品库存剩余量 --》 3个
public static int count = 3;
}
(3)模拟商城消费商品
// 模拟商城消费商品
public class Mall {
public synchronized void sale(){
if(Stock.count > 0){
try{
// 模拟商城办理销售业务 用时3秒
Thread.sleep(3);
}catch (InterruptedException e){
e.printStackTrace();
}
// 销售成功 库存剑少
Stock.count --;
System.out.println("商品销售成功");
}else{
System.out.println("卖完了");
}
}
public static void main(String[] args) {
// 实例化商城唯一对象
Mall mall = new Mall();
// 模拟3名顾客 同时进如商城消费商品
for(int m = 0; m < 6; m++){
Consumer consumer = new Consumer();
consumer.mall = mall;
Thread thread = new Thread(consumer);
thread.start();
}
try{
// 模拟下班后 查看库存
Thread.sleep(1000);
System.out.println("当前商品的库存为:" + Stock.count);
}catch(InterruptedException e){
e.printStackTrace();
}
/**
* 商品销售成功
* 商品销售成功
* 商品销售成功
* 卖完了
* 卖完了
* 卖完了
* 当前商品的库存为:0
*/
}
}
4.线程池
为什么不选用实现Runnable接口,而要使用线程池?
- Runnable需要新建线程,性能较差
- 线程缺乏统一管理
- 可能会无限制的新建线程
- 线程间会相互竞争,严重的情况下会占用过多的系统资源导致死机或者内存溢出
4.1 概述
(1)JUC
并发工具包
(2)JUC支持的线程池种类
在java.util.concurrent中,提供了工具类Executors(调度器) 对象来创建线程池
可创建的线程池有四种:
- 定长线程池
- 可缓存线程池
- 单线程池
- 调度线程池
(3)ThreadPool线程池特点
- 重用存在的线程,减少线程对象创建、消亡的开销
- 线程总数可控,提高资源的利用率。
- 提供额外功能,定时执行、定期执行、监控等
4.2 线程池创建的4中方式
(1)创建定长线程池
/** * 01 创建定长线程池 * * 特点:固定线程总数, 空闲线程用于执行任务。 若线程都在工作,则后续的任务处于等待状态 * * 需求: 创建一个容量为8的线程池,并发打印1~88 */
code实现
/**
* 01 创建定长线程池
*
* 特点:固定线程总数, 空闲线程用于执行任务。 若线程都在工作,则后续的任务处于等待状态
*
* 需求: 创建一个容量为8的线程池,并发打印1~88
*/
public class FixedPool {
public static void main(String[] args) {
ExecutorService threadPool = Executors.newFixedThreadPool(8);
for(int p = 1; p <= 88; p++){
final int p_index = p;
// 通过匿名内部类 快速创建
threadPool.execute(new Runnable(){
@Override
public void run(){
System.out.println(Thread.currentThread().getName() + ":" + p_index);
}
});
}
/**
* pool-1-thread-1:1
* pool-1-thread-6:6
* pool-1-thread-2:2
* pool-1-thread-6:10
* pool-1-thread-6:12
* pool-1-thread-6:13
* pool-1-thread-5:5
* pool-1-thread-5:15
* pool-1-thread-5:16
* ......
*/
threadPool.shutdown();
}
}
(2)创建缓存线程池
/**
* 02 创建可缓存线程池
*
* 特点是: 容量无限大, 若线程池中没有可用的线程则进行创建, 有的话则调度空闲的线程
*
* 需求: 创建一个容量为8的线程池,并发打印1~77
*/
/**
* 02 创建可缓存线程池
*
* 特点是: 容量无限大, 若线程池中没有可用的线程则进行创建, 有的话则调度空闲的线程
*
* 需求: 创建一个容量为8的线程池,并发打印1~77
*/
public class CachedPool {
public static void main(String[] args) {
// ExecutorService 用于管理线程池
ExecutorService threadPool = Executors.newCachedThreadPool(); //创建一个可缓存线程池
for (int p = 0; p <= 77; p++) {
final int p_index = p;
threadPool.execute(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + ":" + p_index);
}
});
}
/**
* pool-1-thread-2:1
* pool-1-thread-6:5
* pool-1-thread-7:6
* pool-1-thread-4:3
* pool-1-thread-8:7
* pool-1-thread-3:2
* pool-1-thread-5:4
* pool-1-thread-9:8
* pool-1-thread-9:12
* pool-1-thread-3:14
* pool-1-thread-10:9
*/
threadPool.shutdown();
}
}
(3)创建单线程线程池
// 03 应用单线程线程池
public class SinglePool {
public static void main(String[] args) {
// 创建单线程线程池
ExecutorService threadPool = Executors.newSingleThreadExecutor();
// 打印 80 到 66
for(int p = 80; p >= 66; p--){
final int finalP = p;
threadPool.execute(new Runnable(){
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + ":" + "finalP:" + finalP);
}
});
}
threadPool.shutdown();
/**
* pool-1-thread-1:finalP:80
* pool-1-thread-1:finalP:79
* pool-1-thread-1:finalP:78
* pool-1-thread-1:finalP:77
* pool-1-thread-1:finalP:76
* pool-1-thread-1:finalP:75
* pool-1-thread-1:finalP:74
* pool-1-thread-1:finalP:73
* pool-1-thread-1:finalP:72
* pool-1-thread-1:finalP:71
* pool-1-thread-1:finalP:70
* pool-1-thread-1:finalP:69
* pool-1-thread-1:finalP:68
* pool-1-thread-1:finalP:67
* pool-1-thread-1:finalP:66
*/
}
}
(4)可调度线程池
// 04 应用可调度线程池
public class ScheduleThreadPool {
public static void main(String[] args) {
// 调度线程池
ScheduledExecutorService scheduledPool = Executors.newScheduledThreadPool(7);
// 延时3秒执行, 每6秒执行一次
scheduledPool.scheduleAtFixedRate(new Runnable(){
@Override
public void run(){
System.out.println(new Date() + "延时3秒执行, 每6秒执行一次");
}
},3,6, TimeUnit.SECONDS);
/**
* Wed Jun 04 18:40:18 CST 2025延时3秒执行, 每6秒执行一次
* Wed Jun 04 18:40:24 CST 2025延时3秒执行, 每6秒执行一次
* Wed Jun 04 18:40:30 CST 2025延时3秒执行, 每6秒执行一次
* Wed Jun 04 18:40:36 CST 2025延时3秒执行, 每6秒执行一次
* Wed Jun 04 18:40:42 CST 2025延时3秒执行, 每6秒执行一次
* Wed Jun 04 18:40:48 CST 2025延时3秒执行, 每6秒执行一次
* Wed Jun 04 18:40:54 CST 2025延时3秒执行, 每6秒执行一次
*/
}
}