十, 多线程
1. 线程
-
进程是程序的基本执行实体
-
线程是操作系统能够进行运算调度的最小单位, 它被包含在进程之中, 是进程中的实际运作单位
-
有了多线程, 可以让程序同时做多种事情 以提高效率
-
并发: 在同一时刻, 有多个指令在单个CPU上交替进行
并行: 在同一时刻, 有多个指令在多个CPU上同时进行
线程的生命周期


2. 多线程的实现方式
2.1 继承Thread类的实现方式
package awei.itheima;
public class MyThread extends Thread{
public void run(){
for(int i = 0; i < 100; i++){
System.out.println(getName() + " " + i);
}
}
}
public static void main(String[] args) throws ParseException, IOException, ClassNotFoundException {
/*
多线程的第一种启动方式:
(1) 自己定义一个类继承线程类(Thread)
(2) 重写run方法
(3) 创建自己的线程类对象
* */
MyThread t1 = new MyThread();
MyThread t2 = new MyThread();
t1.setName("线程1");
t2.setName("线程2");
t1.start();
t2.start();
}
2.2 实现Runnable接口的方式进行实现
package awei.itheima;
public class MyRun implements Runnable {
public void run(){
for(int i = 0; i < 100; i++){
// 获取到当前的线程对象
Thread t = Thread.currentThread();
System.out.println(t.getName() + " " + i);
}
}
}
public static void main(String[] args) throws ParseException, IOException, ClassNotFoundException {
/*
多线程的第二种启动方式:
(1) 自己定义一个类实现Runnable接口
(2) 重写run方法
(3) 创建自己的类对象
(4) 创建Thread类对象,构造方法中传递Runnable接口的实现类对象
* */
MyRun mr = new MyRun();
Thread t1 = new Thread(mr);
Thread t2 = new Thread(mr);
t1.setName("线程1");
t2.setName("线程2");
t1.start();
t2.start();
}
2.3 利用Callable接口和Future接口方式实现
- 前两种方法run方法中返回值类型为void, 不能获得多线程的运行结果
package awei.itheima;
import java.util.concurrent.Callable;
public class MyCallable implements Callable<Integer> {
@Override
public Integer call() throws Exception {
int sum = 0;
for (int i = 0; i < 100; i++) {
sum = sum + i;
}
return sum;
}
}
public static void main(String[] args) throws ParseException, IOException, ClassNotFoundException, ExecutionException, InterruptedException {
/*
多线程的第三种启动方式:
特点: 可以获取到多线程运行的结果
(1) 创建一个类MyCallable实现Callable接口
(2) 重写call()方法(是有返回值的, 表示多线程的运行结果)
(3) 创建MyCallable对象(表示多线程要执行的任务)
(4) 创建FutureTask对象(作用管理多线程的运行结果)
(5) 创建多线程对象, 将FutureTask对象作为构造参数
* */
// 创建MyCallable对象
MyCallable mc = new MyCallable();
// 创建FutureTask对象
FutureTask<Integer> ft = new FutureTask<>(mc);
// 创建多线程对象, 将FutureTask对象作为构造参数
Thread t = new Thread(ft);
t.start();
System.out.println(ft.get());
}
3. 同步
3.1 同步代码块
- 把操作共享数据的代码锁起来
特点一: 锁默认打开, 有一个线程进去了, 锁默认关闭
特点二: 里面的代码全部执行完毕, 线程出来, 锁自动打开
synchronized(锁){
操作共享数据的代码
}
也可以将字节码对象作为唯一的锁

3.2 同步方法
-
同步方法: 就是把synchronized关键字加到方法上
-
格式: 修饰符 synchronized 返回值类型 方法名(方法参数)
-
特点:
- 同步方法是锁住方法里面的所有代码
- 锁对象不能自己指定, 非静态: this 静态: 当前类的字节码文件对象

-
StringBuffer线程安全, StringBuilder线程不安全
4. Lock锁


5. 线程池
5.1 初识
- 线程池主要核心原理:
- 创建一个池子, 池子是空的
- 提交任务时, 池子会创建新的线程对象, 任务执行完毕, 线程归还给池子, 下回再次提交任务时, 不需要创建新的线程, 直接复用已有的线程即可
- 但是如果提交任务时, 池子没有空闲线程, 也无法创建新的线程, 任务就会排队等待
5.2 Java方法创建线程池

package awei.itheima;
public class MyRunnable implements Runnable{
@Override
public void run() {
for (int i = 0; i <= 100; i++) {
System.out.println(Thread.currentThread().getName() + "---" + i);
}
}
}
// 1. 获取线程池对象
ExecutorService pool = Executors.newCachedThreadPool();
// 2. 提交任务
pool.submit(new MyRunnable());
pool.submit(new MyRunnable());
pool.submit(new MyRunnable());
pool.submit(new MyRunnable());
pool.submit(new MyRunnable());
// 3. 销毁线程池对象
pool.shutdown();
5.3 自定义线程池
5.3.1 线程池定义
-
自定义线程池: (1) 创建一个空的池子 (2) 有任务提交时, 线程池会创建线程去执行任务, 执行完毕归还线程
-
不断地提交任务, 会有以下三个临界点:
- 当核心线程满时, 再提交任务会排队
- 当核心线程满, 队伍满时, 会创建临时线程
- 当核心线程满, 临时线程满, 队伍满时, 会触发任务拒绝策略


/*
ThreadPoolExecutor pool = new ThreadPoolExecutor();
参数1: 核心线程数量 不能小于0
参数2: 最大线程数量 不能小于等于0, 最大数量>=核心线程数量
参数3: 空闲线程最大存活的时间 不能小于0
参数4: 时间单位 用TimeUnit指定
参数5: 阻塞队列 不能为null
参数6: 创建线程工厂 不能为null
参数7: 拒绝策略 不能为null
*/
ThreadPoolExecutor pool = new ThreadPoolExecutor(
3, // 核心线程数量
6, // 最大线程数量
60,
TimeUnit.SECONDS,
new ArrayBlockingQueue<>(3),
Executors.defaultThreadFactory(),
new ThreadPoolExecutor.AbortPolicy() // 内部类,依赖
);
5.3.2 线程池多大合适


浙公网安备 33010602011771号