十, 多线程

1. 线程

  1. 进程是程序的基本执行实体

  2. 线程是操作系统能够进行运算调度的最小单位, 它被包含在进程之中, 是进程中的实际运作单位

  3. 有了多线程, 可以让程序同时做多种事情 以提高效率

  4. 并发: 在同一时刻, 有多个指令在单个CPU上交替进行
    并行: 在同一时刻, 有多个指令在多个CPU上同时进行

线程的生命周期

image
image

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接口方式实现

  1. 前两种方法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 同步代码块

  1. 把操作共享数据的代码锁起来
    特点一: 锁默认打开, 有一个线程进去了, 锁默认关闭
    特点二: 里面的代码全部执行完毕, 线程出来, 锁自动打开
    synchronized(锁){
        操作共享数据的代码
    }

也可以将字节码对象作为唯一的锁
image

3.2 同步方法

  1. 同步方法: 就是把synchronized关键字加到方法上

  2. 格式: 修饰符 synchronized 返回值类型 方法名(方法参数)

  3. 特点:

    • 同步方法是锁住方法里面的所有代码
    • 锁对象不能自己指定, 非静态: this 静态: 当前类的字节码文件对象
      image
  4. StringBuffer线程安全, StringBuilder线程不安全

4. Lock锁

image
image

5. 线程池

5.1 初识

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

5.2 Java方法创建线程池

image

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. 自定义线程池: (1) 创建一个空的池子 (2) 有任务提交时, 线程池会创建线程去执行任务, 执行完毕归还线程

  2. 不断地提交任务, 会有以下三个临界点:

    • 当核心线程满时, 再提交任务会排队
    • 当核心线程满, 队伍满时, 会创建临时线程
    • 当核心线程满, 临时线程满, 队伍满时, 会触发任务拒绝策略
      image
      image
        /*
            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 线程池多大合适

image

posted @ 2025-08-14 19:53  awei040519  阅读(6)  评论(0)    收藏  举报