Java多线程

多线程相关概念

  • 线程
    线程是操作系统能够进行运算调度的最小单位。它被包含在进程之中是进程中的实际运作单位
  • 进程
    进程是程序的基本执行实体
  • 作用
    提供效率
  • 并发
    在同一时刻有多个指令在单个CPU上交替执行
  • 并行
    在同一时刻有多个指令在多个CPU上同时执行

多线程的实现方式

1、继承Thread类的方式进行实现

  • main方法
public class ThreadDemo {
    public static void main(String[] args) {
        /*
        多线程的第一种启动方式
        1、自己定义一个类继承Thread
        2、重写run方法
        3、创建子类的对象并启动线程
         */
        MyThread t1 = new MyThread();
        MyThread t2 = new MyThread();
        t1.setName("线程1: ");
        t2.setName("线程2: ");
        t1.start();
        t2.start();
    }
}
  • 继承Thread类
public class MyThread extends Thread{
    @Override
    public void run(){
        //书写线程要执行的代码
        for (int i = 0; i < 100; i++) {
            System.out.println(getName() + "Hello World");
        }
    }
}

2、实现Runnable接口的方式进行实现

  • main方法
public class RunnableDemo {
    public static void main(String[] args) {
        /*
        多线程的第二种启动方式
        1、自己定义一个类实现Runnable接口
        2、重写里面的run方法
        3、参加自己的类的对象
        4、参加一个Thread类对象并开启线程
         */
        //创建MyRunnable的对象
        //表示多线程要执行的任务
        MyRunnable mr = new MyRunnable();
        //创建线程对象
        Thread t1 = new Thread(mr);
        Thread t2 = new Thread(mr);
        //给线程起名字
        t1.setName("线程1: ");
        t2.setName("线程2: ");
        //开启线程
        t1.start();
        t2.start();
    }
}
  • 实现MyRunnable接口
public class MyRunnable implements Runnable{
    @Override
    public void run() {
        //书写线程要执行的代码
        //获取当前线程对象
        //Thread t = Thread.currentThread();
        for (int i = 0; i < 100; i++) {
            //System.out.println(t.getName() + "Hello World");
            System.out.println(Thread.currentThread().getName() + "Hello World");
        }
    }
}

3、利用Callable接口和Future接口方式实现

  • main方法
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
public class CallableDemo {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        /*
        多线程的第三种实现方式
        特点可以获取到多线程运行的结果
        1、创建一个类MyCallable实现Callable接口
        2、重写call是有返回值的表示多线程运行的结果
        3、创建MyCallable的对象表示多线程要执行的任务
        4、创建FutureTask的对象表示管理多线程运行的结果
        5、创建Thread类的对象表示线程
         */
        //创建MyCallable的对象表示多线程要执行的任务
        MyCallable mc = new MyCallable();
        //创建FutureTask的对象表示管理多线程运行的结果
        FutureTask<Integer> ft = new FutureTask<>(mc);
        //创建Thread类的对象表示线程
        Thread t = new Thread(ft);
        //启动线程
        t.start();
        //获取多线程运行的结果
        Integer result = ft.get();
        System.out.println(result);
    }
}
  • 实现Callable接口
import java.util.concurrent.Callable;
public class MyCallable implements Callable<Integer> {
    @Override
    public Integer call() throws Exception {
        //求1~100之间的和
        int sum = 0;
        for (int i = 0; i <= 100; i++) {
            sum += i;
        }
        return sum;
    }
}
  • 多线程三种实现方式对比
    在这里插入图片描述

Thread类常见的成员方法

在这里插入图片描述

  • 演示方法getName、setName、currentThread、sleep

  • main方法

public class ThreadDemo1 {
    public static void main(String[] args) throws InterruptedException {
        /*
        给线程设置名字细节:
        1、如果我们没有给线程设置名字线程也是有默认的名字的
                        格式Thread-XX序号从0开始
        2、如果我们要给线程设置名字可以用set方法设置也可以构造方法设置
         */
        //1、创建线程对象
        MyThread1 t1 = new MyThread1("线程1");
        MyThread1 t2 = new MyThread1("线程2");
        //2、开启线程
        //t1.start();
        //t2.start();
        /*
        currentThread细节
        1、当JVM虚拟机启动之后会自动的启动多条线程
        2、其中有一条线程就是main线程它的作用就是去调用main方法并执行里面的代码
         */
        Thread t = Thread.currentThread();
        String name = t.getName();
        System.out.println(name);//main
        /*
        sleep方法细节
        1、哪条线程执行到这个方法那么哪条线程就会在这里停留对应的时间
        2、方法的参数就表示睡眠的时间单位毫秒 1秒=1000毫秒
        3、当时间到了之后线程会自动的醒来继续执行下面的其它代码
         */
        System.out.println("abc");
        Thread.sleep(5000);
        System.out.println("def");
    }
}
  • MyThread类
public class MyThread1 extends Thread{
    public MyThread1() {
    }

    public MyThread1(String name) {
        super(name);
    }

    @Override
    public void run(){
        for (int i = 0; i < 100; i++) {
            System.out.println(getName() + "@" + i);
        }
    }
}

线程的优先级

  • main方法
public class ThreadDemo2 {
    public static void main(String[] args){
      MyThread2 t = new MyThread2();
      Thread t1 = new Thread(t,"线程1");
      Thread t2 = new Thread(t,"线程2");
      //设置线程的优先级
      t1.setPriority(1);
      t2.setPriority(10);
      t1.start();
      t2.start();
    }
}
  • MyThread2类
public class MyThread2 implements Runnable{
    @Override
    public void run() {
        for (int i = 1; i <= 100; i++) {
            System.out.println(Thread.currentThread().getName() + "---" + i);
        }
    }
}

守护线程

  • main方法
public class ThreadDemo3 {
    public static void main(String[] args){
        /*
        setDaemon细节
        当其它的非守护线程执行完毕后守护线程会陆续结束
        也就是说t3结束了t4也就没有存在的必要了
        因为t3循环次数少于t4一般来说t4循环
        不到100次就会提前结束
         */
      MyThread3 t3 = new MyThread3();
      MyThread4 t4 = new MyThread4();
      t3.setName("线程3");
      t4.setName("线程4");
      //把第二个线程设置为守护线程
        t4.setDaemon(true);
        
        t3.start();
        t4.start();
    }
}
  • MyThread3
public class MyThread3 extends Thread{
    @Override
    public void run() {
        for (int i = 1; i <= 10; i++) {
            System.out.println(getName() + "---" + i);
        }
    }
}
  • MyThread4
public class MyThread4 extends Thread {
    @Override
    public void run() {
        for (int i = 1; i <= 100; i++) {
            System.out.println(getName() + "---" + i);
        }
    }
}

线程礼让和插入线程

  • 礼让线程
  • main方法
public class ThreadDemo5 {
    public static void main(String[] args){
        MyThread5 t1 = new MyThread5();
        MyThread5 t2 = new MyThread5();
        t1.setName("线程1");
        t2.setName("线程2");
        t1.start();
        t2.start();
    }
}
  • MyThread5类
public class MyThread5 extends Thread {
    @Override
    public void run() {
        for (int i = 1; i <= 100; i++) {
            System.out.println(getName() + "---" + i);
            //表示让出当前CPU的执行权
            Thread.yield();
        }
    }
}
  • 插入线程
  • main方法
public class ThreadDemo6 {
    public static void main(String[] args) throws InterruptedException {
        MyThread6 t = new MyThread6();
        t.setName("线程1");
        t.start();
        //表示把t这个线程插入到当前线程之前
        //t线程1
        //当前线程main
        t.join();
        //执行在main线程当中
        for (int i = 1; i <= 10; i++) {
            System.out.println("main" + "---" + i);
        }
    }
}
  • MyThread6类
public class MyThread6 extends Thread {
    @Override
    public void run() {
        for (int i = 1; i <= 100; i++) {
            System.out.println(getName() + "---" + i);
        }
    }
}

线程的生命周期

在这里插入图片描述

同步代码块

  • 作用把操作共享数据的代码锁起来
- 格式
synchronized(锁){
      操作共享数据的代码
}
  • main方法
public class ThreadDemo7 {
    public static void main(String[] args){
        MyThread7 t1 = new MyThread7();
        MyThread7 t2 = new MyThread7();
        MyThread7 t3 = new MyThread7();
        t1.setName("窗口1");
        t2.setName("窗口2");
        t3.setName("窗口3");
        t1.start();
        t2.start();
        t3.start();
    }
}
  • MyThread7类
public class MyThread7 extends Thread {
    //表示这个类的所有对象都共享ticket数据
    static int ticket = 0;
    //锁对象一定要是唯一的
    static Object obj = new Object();
    @Override
    public void run() {
        while (true){
            //同步代码块
            synchronized (obj) {//也可以是MyThread7.class因为该类的字节码文件是唯一的
                if (ticket < 100) {
                    try {
                        Thread.sleep(10);
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                    ticket++;
                    System.out.println(getName() + "正在卖第" + ticket + "张票");
                } else {
                    break;
                }
            }
        }
    }
}

同步方法

就是把synchronized关键字加到方法上

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

特点1同步方法是锁住方法里面所有的代码
特点2锁对象不能自己指定
非静态this
静态当前类的字节码文件对象

  • main方法
public class RunnableDemo1 {
    public static void main(String[] args) {
        MyRunnable1 mr = new MyRunnable1();

        Thread t1 = new Thread(mr);
        Thread t2 = new Thread(mr);
        Thread t3 = new Thread(mr);

        t1.setName("窗口1");
        t2.setName("窗口2");
        t3.setName("窗口3");

        t1.start();
        t2.start();
        t3.start();
    }
}
  • MyRunnable1类
public class MyRunnable1 implements Runnable {
    int ticket = 0;
    @Override
    public void run() {
        //1、循环
        while (true) {
            //2、同步代码块
            try {
                if (method()) break;
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    }
    private synchronized boolean method() throws InterruptedException {
        //3、判断共享数据是否到了末尾
            if (ticket == 100) {
                return true;
            } else {
                //4、判断共享数据是否到了末尾如果没有到末尾
                Thread.sleep(10);
                ticket++;
                System.out.println(Thread.currentThread().getName()
                        + "正在卖第" + ticket + "票");
            }
        return false;
    }
}

Lock锁

  • 作用Lock实现提供比使用synchronize方法和语句可以获得更广泛的锁定操作
- Lock中提供了获得锁和释放锁的方法
void lock(): 获得锁
void unlock(): 释放锁
  • 注意
    Lock是接口不能直接实例化这里采用它的实现类ReentrantLock来实例化
    ReentrantLock的构造方法
    ReentrantLock(): 创建一个ReentrantLock的实例
  • main方法
public class ThreadDemo8 {
    public static void main(String[] args){
        MyThread8 t1 = new MyThread8();
        MyThread8 t2 = new MyThread8();
        MyThread8 t3 = new MyThread8();
        t1.setName("窗口1");
        t2.setName("窗口2");
        t3.setName("窗口3");
        t1.start();
        t2.start();
        t3.start();
    }
}
  • MyThread8类演示Lock锁
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class MyThread8 extends Thread {
    //表示这个类的所有对象都共享ticket数据
    static int ticket = 0;
    //共享一个锁对象要加static关键字
    static Lock lock = new ReentrantLock();
    @Override
    public void run() {
        while (true){
            //同步代码块
            //synchronized (MyThread8.class) {
            lock.lock();
            try {
                if (ticket < 100) {
                        Thread.sleep(10);
                    ticket++;
                    System.out.println(getName() + "正在卖第" + ticket + "张票");
                } else {
                    break;
                }
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            } finally {
                lock.unlock();//防止在100次循环直接break掉没有释放锁
                // 所以将释放锁的代码放在finally语句中
            }
            //}
        }
    }
}

等待唤醒机制生产者和消费者

在这里插入图片描述

  • 生产者消费者模式是一个十分经典的多线程协助的模式

常见方法
在这里插入图片描述

  • main方法测试
public class ThreadDemo9 {
    public static void main(String[] args) {
        //创建线程对象
        Cook c = new Cook();
        Foodie f = new Foodie();
        //给线程设置名字
        c.setName("生产者");
        f.setName("消费者");
        //开启线程
        c.start();
        f.start();
    }
}

Cook代码生产者

public class Cook  extends Thread{
    @Override
    public void run() {
        /*
    1、循环
    2、同步代码块
    3、判断共享数据是否到了末尾到了末尾
    4、判断共享数据是否到了末尾没有到末尾执行核心逻辑
     */
        while (true){
            synchronized (Desk.lock){
                if(Desk.count == 0){
                    break;
                }else {
                    //判断桌子上是否有食物
                    if(Desk.foodFlag == 1){
                        //如果有就等待
                        try {
                            Desk.lock.wait();
                        } catch (InterruptedException e) {
                            throw new RuntimeException(e);
                        }
                    }else {
                        //如果没有就制作食物
                        System.out.println("厨师做了一碗面条");
                        //修改桌子上的食物状态
                        Desk.foodFlag = 1;
                        //叫醒等待的消费者开吃
                        Desk.lock.notifyAll();
                    }
                }
            }
        }
    }
}
  • Foodie代码消费者
public class Foodie extends Thread {
    @Override
    public void run() {
    /*
    1、循环
    2、同步代码块
    3、判断共享数据是否到了末尾到了末尾
    4、判断共享数据是否到了末尾没有到末尾执行核心逻辑
     */
        while (true){
            synchronized (Desk.lock){
                if(Desk.count == 0){
                    break;
                }else {
                    //先判断桌子上是否有面条
                    if(Desk.foodFlag == 0){
                        //如果没有就等待
                        try {
                            Desk.lock.wait();//让当前线程跟锁进行判定
                        } catch (InterruptedException e) {
                            throw new RuntimeException(e);
                        }
                    }else {
                        //把吃的总数-1
                        Desk.count--;
                        //如果有就开吃
                        System.out.println("吃货正在吃面条还能再吃" + Desk.count + "碗");
                        //吃完之后唤醒厨师继续做
                        Desk.lock.notifyAll();
                        //修改桌子的状态
                        Desk.foodFlag = 0;
                    }
                }
            }
        }
    }
}
  • Desk代码控制生产者和消费者
public class Desk {
    /*
    作用控制生产者和消费者的执行
     */
    //是否有面条 0没有面条 1有面条
    public static int foodFlag = 0;
    //总个数
    public static int count = 10;
    //锁对象
    public static Object lock = new Object();
}

等待唤醒机制阻塞队列方式实现

  • put数据时放不进去会等着也叫做阻塞
  • take数据时取出第一个数据取不到会等着也叫做阻塞

main方法测试

import java.util.concurrent.ArrayBlockingQueue;
public class ThreadDemo10 {
    public static void main(String[] args) {
        /*
        需求利用阻塞队列完成生产者和消费者的代码
         */
        //细节生产者和消费者必须使用同一个阻塞队列
        //1、创建阻塞队列的对象
        ArrayBlockingQueue<String> queue = new ArrayBlockingQueue<>(1);
        //2、创建线程对象并把阻塞队列传递过去
        Cook2 c = new Cook2(queue);
        Foodie2 f = new Foodie2(queue);
        //3、开启线程
        c.start();
        f.start();
    }
}
  • Cook2类生产者
import java.util.concurrent.ArrayBlockingQueue;
public class Cook2 extends Thread {
        ArrayBlockingQueue<String> queue;

        public Cook2(ArrayBlockingQueue<String> queue) {
            this.queue = queue;
        }
        @Override
        public void run() {
            while (true){
                //不断的把面条放到阻塞队列当中
                try {
                    queue.put("面条");
                    System.out.println("厨师做了一碗面条");
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        }
    }
  • Foodie消费者
import java.util.concurrent.ArrayBlockingQueue;
public class Foodie2 extends Thread{
    ArrayBlockingQueue<String> queue;
    public Foodie2(ArrayBlockingQueue<String> queue){
        this.queue = queue;
    }
    @Override
    public void run() {
        while (true){
            //不断从阻塞队列中获取面条
            String food = null;
            try {
                food = queue.take();
                System.out.println(food);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    }
}

线程的状态

在这里插入图片描述

  • 注意线程只有6种状态没有运行状态
    在这里插入图片描述

综合练习

1、抢红包
假设100块分成了3个包现在有5个人去抢
其中红包是共享数据
5个人是5条线程
打印结果如下
×××抢到了×××元
×××抢到了×××元
×××抢到了×××元
×××没抢到
×××没抢到

  • main方法测试
public class Test1 {
    public static void main(String[] args) {
        //创建线程的对象
        MyThread9 t1 = new MyThread9();
        MyThread9 t2 = new MyThread9();
        MyThread9 t3 = new MyThread9();
        MyThread9 t4 = new MyThread9();
        MyThread9 t5 = new MyThread9();
        //给线程设置名字
        t1.setName("线程1");
        t2.setName("线程2");
        t3.setName("线程3");
        t4.setName("线程4");
        t5.setName("线程5");
        //启动线程
        t1.start();
        t2.start();
        t3.start();
        t4.start();
        t5.start();
    }
}
  • MyThread9类
import java.util.Random;
public class MyThread9 extends Thread{
    //共享数据
    //100块分成3个包
    static double money = 100;
    static int count = 3;
    //最小的中奖金额
    static final double MIN = 0.01;
    @Override
    public void run() {
       synchronized (MyThread9.class){
           if(count == 0){
               System.out.println(getName() + "没有抢到红包");
           }else {
               //判断共享数据是否到了末尾没有到末尾
               //定义一个变量表示中奖的金额
               double prize = 0;
               if(count == 1){
                   //表示此时是最后一个红包
                   //就无需随机剩余所有的钱都是中奖金额
                   prize = money;
               }else {
                   //表示第一次第二次随机
                   Random r = new Random();
                   //100元   3个包
                   //第一个红包99.98
                   //100 - (3-1) * 0.01
                   double bounds = money - (count - 1) * MIN;
                   prize = r.nextDouble(bounds);
                   if(prize < MIN){
                       prize = MIN;
                   }
               }
               //从money当中去掉当前中奖的金额
               money = money - prize;
               //红包的个数-1
               count--;
               //本次红包的信息进行打印
               System.out.println(getName() + "抢到了" + prize + "元");
           }
       }
    }
}

2、抽奖箱抽奖
有一个抽奖池该抽奖池中存放了奖励的金额该抽奖池中的奖项为
{10,5,20,50,100,200,500,800,2,80,300,700};
创建两个抽奖箱(线程)设置线程名称分别为“抽奖箱1”“抽奖箱2”
随机从抽奖池中获取奖项元素并打印在控制台上格式如下
每次抽出一个奖项就打印一个随机
抽奖箱1又产生了一个10元大奖
抽奖箱1又产生了一个100元大奖
抽奖箱1又产生了一个200元大奖
抽奖箱1又产生了一个800元大奖
抽奖箱2又产生了一个700元大奖

  • main方法测试
import java.util.ArrayList;
import java.util.Collections;
public class Test2 {
    public static void main(String[] args) {
        //创建奖池
        ArrayList<Integer> list = new ArrayList<>();
        Collections.addAll(list,10,5,20,50,100,200,500,800,2,80,300,700);
        //创建线程
        MyThread10 t1 = new MyThread10(list);
        MyThread10 t2 = new MyThread10(list);
        //设置名称
        t1.setName("抽奖箱1");
        t2.setName("抽奖箱2");
        //启动线程
        t1.start();
        t2.start();
    }
}
  • MyThread10类
import java.util.ArrayList;
import java.util.Collections;
public class MyThread10 extends Thread{
    ArrayList<Integer> list;
    public MyThread10(ArrayList<Integer> list){
        this.list = list;
    }
    @Override
    public void run() {
        while (true) {
            synchronized (MyThread10.class) {
                if (list.size() == 0) {
                    break;
                }else {
                    //继续抽奖
                    Collections.shuffle(list);
                    int prize = list.remove(0);
                    System.out.println(getName() + "产生了一个" + prize + "元大奖");
                }
            }
            try {
                Thread.sleep(10);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    }
}

3、在第二题的基础上继续完成如下需求
每次抽的过程中不打印抽完时一次性打印随机
在这次抽奖过程中抽奖箱1总共产生了6个奖项。
分别为10,20,100,500,2,300最高奖项为300元总计金额为932元
这次抽奖过程中抽奖箱2总共产生了6个奖项。
分别为5,50,200,800,80,700最高奖项为800元总计金额为1835元

  • main方法测试
import java.util.ArrayList;
import java.util.Collections;
public class Test3 {
    public static void main(String[] args) {
        //创建奖池
        ArrayList<Integer> list = new ArrayList<>();
        Collections.addAll(list,10,5,20,50,100,200,500,800,2,80,300,700);
        //创建线程
        MyThread11 t1 = new MyThread11(list);
        MyThread11 t2 = new MyThread11(list);
        //设置名称
        t1.setName("抽奖箱1");
        t2.setName("抽奖箱2");
        //启动线程
        t1.start();
        t2.start();
    }
}
  • MyThread11类线程栈的方式实现
import java.util.ArrayList;
import java.util.Collections;
public class MyThread11 extends Thread{
    ArrayList<Integer> list;
    public MyThread11(ArrayList<Integer> list){
        this.list = list;
    }
    @Override
    public void run() {
        ArrayList<Integer> boxList = new ArrayList<>();//1 //2
        while (true) {
            synchronized (MyThread11.class) {
                if (list.size() == 0) {
                    System.out.println(getName() + boxList);
                    break;
                }else {
                    //继续抽奖
                    Collections.shuffle(list);
                    int prize = list.remove(0);
                    boxList.add(prize);
                }
            }
            try {
                Thread.sleep(10);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    }
}

4、在第三题的基础上继续完成如下需求
在此次抽奖过程中抽奖箱2中产生了最大奖项该奖项金额为800元

  • main方法
import java.util.ArrayList;
import java.util.Collections;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
public class Test4 {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        //创建奖池
        ArrayList<Integer> list = new ArrayList<>();
        Collections.addAll(list,10,5,20,50,100,200,500,800,2,80,300,700);
        //创建多线程要运行的参数对象
        MyCallable1 mc = new MyCallable1(list);
        //创建多线程运行结果的管理者对象
        //线程1
        FutureTask<Integer> ft1 = new FutureTask<>(mc);
        //线程2
        FutureTask<Integer> ft2 = new FutureTask<>(mc);
        //创建线程
        Thread t1 = new Thread(ft1);
        Thread t2 = new Thread(ft2);
        //设置名称
        t1.setName("抽奖箱1");
        t2.setName("抽奖箱2");
        //启动线程
        t1.start();
        t2.start();

        int max1 = ft1.get();
        int max2 = ft2.get();
        if(max1 == max2){
            System.out.println("在此次抽奖过程中抽奖箱1和抽奖箱2都产生了最大奖项"+max1+"元");
        } else if (max1 > max2) {
            System.out.println("在此次抽奖过程中抽奖箱1产生了最大奖项"+max1+"元");
        }else {
            System.out.println("在此次抽奖过程中抽奖箱2产生了最大奖项"+max2+"元");
        }
    }
}
  • MyCallable1类使用Callable创建线程获取返回值
import java.util.ArrayList;
import java.util.Collections;
import java.util.concurrent.Callable;
public class MyCallable1 implements Callable<Integer> {
    ArrayList<Integer> list;
    public MyCallable1(ArrayList<Integer> list) {
        this.list = list;
    }
    @Override
    public Integer call() throws Exception {
        ArrayList<Integer> boxList = new ArrayList<>();//1 //2
        while (true) {
            synchronized (MyCallable1.class) {
                if (list.size() == 0) {
                    System.out.println(Thread.currentThread().getName() + boxList);
                    break;
                }else {
                    //继续抽奖
                    Collections.shuffle(list);
                    int prize = list.remove(0);
                    boxList.add(prize);
                }
            }
                Thread.sleep(10);
        }
        if(boxList.size() == 0){
            return null;
        }else {
            return Collections.max(boxList);
        }
    }
}

线程池

  • 线程池核心原理
    1、创建一个池子池子是空的
    2、提交任务时池子会创建新的线程对象任务执行完毕线程归还给池子下回再次提交任务时不需要创建新的线程直接复用已有的线程即可
    3、但是如果提交任务时池子中没有空闲线程也无法创建新的线程任务就会排队等待
  • 线程池代码实现
    1、创建线程池
    2、提交任务
    3、所有的任务全部执行完毕关闭线程池

Executors线程池的工具类通过调用方法返回不同类型的线程池对象。
在这里插入图片描述

  • main方法
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class MyThreadPoolDemo {
    public static void main(String[] args) {
        //1、获取线程池对象
        ExecutorService pool1 = Executors.newFixedThreadPool(3);
        //ExecutorService pool2 = Executors.newCachedThreadPool();//没有上限的线程池
        //2、提交任务
        pool1.submit(new MyRunnable2());
        pool1.submit(new MyRunnable2());
        pool1.submit(new MyRunnable2());
        pool1.submit(new MyRunnable2());//排队等待
        //3、销毁线程池
        pool1.shutdown();
    }
}
  • MyRunnable2类
public class MyRunnable2 implements Runnable{
    @Override
    public void run() {
        for (int i = 1; i <= 100; i++) {
            System.out.println(Thread.currentThread().getName() + "---" + i);
        }
    }
}

自定义线程池

任务拒绝策略
在这里插入图片描述
在这里插入图片描述

import java.util.concurrent.*;
public class MyThreadPoolDemo1 {
    public static void main(String[] args) {
        ThreadPoolExecutor pool = new ThreadPoolExecutor(
                3,//核心线程数量不能小于0
                6,//最大线程数不能小于0最大数量 >= 核心线程数量
                60,//空闲线程最大存活时间
                TimeUnit.SECONDS,//时间单位
                new ArrayBlockingQueue<>(3),//任务队列
                Executors.defaultThreadFactory(),//创建线程工厂
                new ThreadPoolExecutor.AbortPolicy()//任务的拒绝策略
        );
    }
}

小结
1、创建一个空的池子
2、有任务提交时线程池会创建线程去执行任务执行完毕归还线程

  • 不断的提交任务会有以下三个临界点
    1、当核心线程满时再提交任务就会排队
    2、当核心线程满队伍满时会创建临时线程
    3、当核心线程满队伍满临时线程满时会触发任务拒绝策略
  • 线程池多大合适
public class MyThreadPoolDemo3 {
    public static void main(String[] args) {
       //向Java虚拟机返回可用处理器的数目
        int count = Runtime.getRuntime().availableProcessors();
        System.out.println(count);//12最大并行数
    }
}

在这里插入图片描述

阿里云国内75折 回扣 微信号:monov8
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6
标签: Java