Java多线程如何保证线程池里的线程都执行完毕

背景:

项目中为了提高性能常常会引入多线程,当我们使用线程池时有时要满足“当线程池里的线程都执行完毕后才能进行下一步”这种业务场景,例如:当多线程操作一个文件时要保证所有线程都运行完毕才能保证文件的完整;当用多线程给一个对象的多个属性进行赋值时要保证线程都运行完毕才将此对象返回,否则就会出现对象属性不全。

实现方式:

1、使用isTerminated方法

2、使用CountDownLatch

3、使用awaitTermination方法

实现代码:

准备:

1、创建Phone对象,属性:color、price;

2、创建People对象,属性:name、age、Phone。

3、有个List<Phone>,里面有

phone1:red、1000;

phone2:blue、2000

phone3:black、3000

4、有个List<People>,里面有

zhangsan,20岁

lisi,21岁

wangwu,22岁

要求:

用多线程实现给zhangsan指定phone1、lisi指定phone2、wangwu指定phone3。

Phone:
public class Phone {
    //手机颜色
    private String color;
    //手机价格
    private double price;

    public Phone() {
    }

    public Phone(String color, double price) {
        this.color = color;
        this.price = price;
    }

    public String getColor() {
        return color;
    }

    public void setColor(String color) {
        this.color = color;
    }

    public double getPrice() {
        return price;
    }

    public void setPrice(double price) {
        this.price = price;
    }

    @Override
    public String toString() {
        return "Phone{" +
                "color='" + color + '\'' +
                ", price=" + price +
                '}';
    }
}
People :
class People {
    private String name;
    private int age;
    private Phone phone;

    public People() {
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public Phone getPhone() {
        return phone;
    }

    public void setPhone(Phone phone) {
        this.phone = phone;
    }

    @Override
    public String toString() {
        return "People{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", phone=" + phone +
                '}';
    }
}

CodeDemo:

public class CodeDemo {
    //创建线程池,核心线程数:2;最大线程数:4;时间:5;时间单位:秒;阻塞队列:ArrayBlockingQueue,最大容量为10;线程工厂:默认;拒绝策略:默认
    static ThreadPoolExecutor poolExecutor = new ThreadPoolExecutor(2, 4, 5, TimeUnit.SECONDS, new ArrayBlockingQueue<>(10));

    public static void main(String[] args) {
        //执行前准备
        List<Phone> phones = getPhoneList();
        List<People> peoples = getPeopleList();
        //使用isTerminated方法
        executorThreads1(peoples, phones);
        //使用CountDownLatch
//        executorThreads2(peoples, phones);
        //使用awaitTermination方法
//        executorThreads3(peoples, phones);
        //结束验证赋值是否成功
        peoples.forEach(people -> System.out.println(people));
    }

    /**
     * 使用awaitTermination方法:
     * 当使用awaitTermination时,主线程会处于一种等待的状态,有下面两种情况发生时才会解除等待状态
     * 1、等待线程池中所有的线程都运行完毕后会解除等待,继续运行
     * 2、如果到了设置的超时时间,线程即使没运行完也会解除等待,继续运行
     */
    private static void executorThreads3(List<People> peoples, List<Phone> phones) {
        peoples.forEach(people -> {
            poolExecutor.execute(new Runnable() {
                @Override
                public void run() {
//                    System.out.println("当前线程:" + Thread.currentThread().getName());
                    phones.forEach(phone -> {
                        if ("zhangsan".equals(people.getName()) && "red".equals(phone.getColor())) {
                            people.setPhone(phone);
                        } else if ("lisi".equals(people.getName()) && "blue".equals(phone.getColor())) {
                            people.setPhone(phone);
                        } else if ("wangwu".equals(people.getName()) && "black".equals(phone.getColor())) {
                            people.setPhone(phone);
                        }
                    });
                    //每个线程暂停1S,防止代码运行太快看不出多线程效果
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            });
        });
        poolExecutor.shutdown();
        try {
            //参数:超时时间、时间单位
            poolExecutor.awaitTermination(1, TimeUnit.MINUTES);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    private static void executorThreads2(List<People> peoples, List<Phone> phones) {
        CountDownLatch count = new CountDownLatch(peoples.size());
        peoples.forEach(people -> {
            poolExecutor.execute(new Runnable() {
                @Override
                public void run() {
//                    System.out.println("当前线程:" + Thread.currentThread().getName());
                    phones.forEach(phone -> {
                        if ("zhangsan".equals(people.getName()) && "red".equals(phone.getColor())) {
                            people.setPhone(phone);
                        } else if ("lisi".equals(people.getName()) && "blue".equals(phone.getColor())) {
                            people.setPhone(phone);
                        } else if ("wangwu".equals(people.getName()) && "black".equals(phone.getColor())) {
                            people.setPhone(phone);
                        }
                    });
                    //每个线程暂停1S,防止代码运行太快看不出多线程效果
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    count.countDown();
                }
            });
        });
        poolExecutor.shutdown();
        //await():等待所有count都执行完毕
        try {
            count.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    private static void executorThreads1(List<People> peoples, List<Phone> phones) {
        peoples.forEach(people -> {
            //多线程运行
            poolExecutor.execute(new Runnable() {
                @Override
                public void run() {
//                    System.out.println("当前线程:" + Thread.currentThread().getName());
                    phones.forEach(phone -> {
                        if ("zhangsan".equals(people.getName()) && "red".equals(phone.getColor())) {
                            people.setPhone(phone);
                        } else if ("lisi".equals(people.getName()) && "blue".equals(phone.getColor())) {
                            people.setPhone(phone);
                        } else if ("wangwu".equals(people.getName()) && "black".equals(phone.getColor())) {
                            people.setPhone(phone);
                        }
                    });
                    //每个线程暂停1S,防止代码运行太快看不出多线程效果
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            });
        });
        poolExecutor.shutdown();
        //每隔1s检测是否线程池里的线程都执行完毕,都执行完后才往下继续运行否则一直卡在这里
        while (true) {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            if (poolExecutor.isTerminated()) break;
        }
    }

    private static List<Phone> getPhoneList() {
        ArrayList<Phone> phones = new ArrayList<>();
        //创建phone1
        phones.add(new Phone("red", 1000));
        //创建phone2
        phones.add(new Phone("blue", 2000));
        //创建phone3
        phones.add(new Phone("black", 3000));
        return phones;
    }

    private static List<People> getPeopleList() {
        ArrayList<People> peoples = new ArrayList<>();
        //创建people1
        People people1 = new People();
        people1.setName("zhangsan");
        people1.setAge(20);
        peoples.add(people1);
        //创建people2
        People people2 = new People();
        people2.setName("lisi");
        people2.setAge(21);
        peoples.add(people2);
        //创建people3
        People people3 = new People();
        people3.setName("wangwu");
        people3.setAge(22);
        peoples.add(people3);
        return peoples;
    }
}

 

 

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
JAVA线程基本学习, JAVA多线程的特性= 线程池: 本质上是一个对象池, 用来管理线程资源. 在任务执行前, 需要从线程池拿出线程执行. 在任务执行完成之后, 需要把线程放回线程池. 线程池好处: 降低资源的消耗, 线程本身是一种资源, 创建和销毁都会消耗CPU内存, 频繁的创建和销毁会浪内存. 提高任务执行的响应速度, 任务执行时, 可以不必等线程创建完成之后在执行,可以直接获取线程执行任务. 提高线程的可管理性, 线程不能无限制的创建, 需要进行统一的分配, 调优和监控. 不适用线程池坏处: 频繁的线程创建和销毁会占用更多的CPU和内积. 频繁的创建和销毁会对gc产生比较大的压力. 线程太多,线程切换带来的开销将不可忽视. 线程太少, 多核CPU得不到充分利用, 浪费资源. 线程池实现原理: 分为三部分: 核心线程池 线程池 队列 拒绝策略 主要流程: 1.判断核心线程池是否已满, 如果不是, 则创建线程执行任务 2.如果核心线程池满了, 判断队列是否满了, 如果队列没满, 将任务放在队列 3.如果队列满了, 则判断线程池是否已满, 如果没满, 创建线程执行任务 4.如果线程池满了, 按照拒绝策略对任务进行处理 JDK提供了一个线程池工厂: Executors ,很多工厂方法, 可以创建多种线程池 1.单一线程池 ExecutorService newSingleThreadExecutor = Executors.newSingleThreadExecutor(); 该线程池只有一个线程, 若多个任务被提交到此线程池, 那么会被缓冲到队列,当线程空闲时,按照FIFO的方式进行处理. 2.固定数量线程池 ExecutorService newFixedThreadPool = Executors.newFixedThreadPool(5); 和创建单一线程池类似, 不同的是线程池有多个线程, 可以并行处理任务, 若多个线程任务被提交到此线程池, 会有以下执行过程: 如果线程的数量未达到指定数量, 则创建新线程执行任务. 如果线程池的数量达到了指定数量, 而且此时有线程是空闲的, 则取出空闲线程执行任务. 如果没有线程是空闲的, 则将任务缓冲到队列, 当线程空闲的时候, 按照FIFO的方式进行处理. FIFO: 先进先出 3.带缓冲的线程池线程池, 核心线程池长度为0, 线程池最大长度为Integer.MAX_VALUE.以SynchronousQueue作为等待队列, 从而每次往队列插入一个元素, 必须等待另一个线程从这个队列删除一个元素. 定时调度 4.定时调度的线程池 ExecutorService newCachedThreadPool = Executors.newScheduledThreadPool(); ScheduledExecutorService newScheduledThreadPool = Executors.newScheduledThreadPool(5); ExecutorService newWorkStealingPool = Executors.newWorkStealingPool() 线程池家族 线程池的顶层接口是Executor, 这个接口定义了一个核心方法executor(Runnable command), 这个方法最后被ThreadPoolExecutor类实现, 这个方法用来传入任务, 并且该类是线程池的核心类, 构造方法如下 : public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,BlockingQueue<Runnable> workQueue); public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,BlockingQueue<Runnable> workQueue,ThreadFactory threadFactory); public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,BlockingQueue<Runnable> workQueue,RejectedExecutionHandler handler); public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,BlockingQueue<Runnable> workQueu, ThreadFactory threadFactory,RejectedExecutionHandler handler); 参数意义: CorePoolSize: 核心线程池大小, 如果核心线程池有空闲的位置, 新的任务就会被核心线程池新建一个线程执行, 执行完毕不会销毁线程, 线程会进入缓冲队列等待再次被运行 MaximunPoolSize: 线程池能创建最大的线程数量, 如果核心线程池和缓冲队列都已经满了, 新的任务就会进来创建新的线程执行, 但是数量不能超过maximunPoolSize, 否则采取拒绝接受任务策略 KeepAliveTime: 非核心线程能够空闲的最长时间, 超过时间, 线程终止, 这个参数默认只有在线程数量超过核心线程池大小时, 才会起作用. Unit: 时间单位, 和keepAliveTime配合使用. WorkQueue: 缓冲队列, 用来存放等待被执行的任务 ThreadFactory: 线程工厂, 用来创建线程, 一般有三个选择 ArrayBlockingQueue LinkedBlockingQueue SynchronousQueue Handler :拒绝处理策略, 线程数量大于最大线程数量就会拒绝处理策略, 四种策略为 ThreadPoolExecutor.AbortPolicy:丢弃任务并抛出RejectedExecutionException异常。 ThreadPoolExecutor.DiscardPolicy:也是丢弃任务,但是不抛出异常。 ThreadPoolExecutor.DiscardOldestPolicy:丢弃队列最前面的任务,然后重新尝试执行任务(重复此过程) ThreadPoolExecutor.CallerRunsPolicy:由调用线程处理该任务 Executor接口有一个子接口ExecutorService, ExecutorService的实现类AbstracExecutorService, 而ThreadPoolExecutor正是AbstracExecutorService的子类. ThreadPoolExecutor还有两个常用的方法shutdown和submit,两者都用来关闭线程池, 但是submit有一个结果返回. 线程池任务执行执行executor(Runnable command)方法后, 传入一个任务, public void execute(Runnable command) { if (command == null) throw new NullPointerException(); if (poolSize >= corePoolSize || !addIfUnderCorePoolSize(command)) { if (runState == RUNNING && workQueue.offer(command)) { if (runState != RUNNING || poolSize == 0) ensureQueuedTaskHandled(command); } else if (!addIfUnderMaximumPoolSize(command)) reject(command); } } (传入的对象实现了Runnable接口, 这就解释了: 为什么实现多线程的方式继承Thread类不能使用线程池了) 执行过程: 首先判断任务是否为空, 为空抛出空指针异常, 否则执行下一个判断: 当前线程数量是否小于核心线程池线程数量, 是,则执行addIfUbderCorePollSize(command)方法, 在核心线程池创建新的线程, 并且执行这个任务 总结: 如果当前线程池的数目小于corePollSize, 则每来一个任务, 就会创建一个新的线程执行这个任务 如果当前线程池线程数目>=corePollSize, 则每来一个任务, 会尝试将其添加到缓冲队列, 如果添加成功, 则该任务会等待空闲线程将其取出去执行, 如果添加失败(一般是以为任务队列已经满了), 则会尝试创建新的线程执行这个任务. 如果当前线程池线程数目达到maximunPoolSize, 则会采取任务拒绝策略进行处理. 如果线程池线程数量大于corePoolSize时, 如果某线程空闲时间超过keepAliveTime, 线程将被终止, 直至线程池线程数目小于等于corePoolSize; 如果允许核心池线程设置存活时间, 那么核心池线程空闲时间超过keepAliveTime, 线程也会被终止.

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值