线程池的创建

线程的缺点

1.线程的创建需要开辟内存资源:本地方法栈、虚拟机栈、程序计数器等线程私有变量的内存。频繁的创建和销毁,会带来一定的性能的开销。
2、使用线程不能很好的管理任务和友好的拒绝任务。

所有就引入了线程池这一技术。

线程池

线程池定义:使用池化技术来管理和使用线程的技术。这种技术就叫线程池。

线程池里面的重要内容:
1、线程
2、任务队列

线程池的执行流程:当拿到一个任务之后,会判断当前线程池里面的数量是否达到了最大值,如果没有达到创建新的线程执行任务;当任务来了之后,线程池的线程数量已经是最大值,并且没有空闲的线程,当前的任务会被放到线程池的任务队列里等待。

线程池的七种创建方式

第一种:创建固定个数的线程池。(任务数趋向无限大,建议谨慎使用)

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class ThreadPoolDemo1 {
    public static void main(String[] args) {
        //创建固定个数的线程池
        ExecutorService service = Executors.newFixedThreadPool(10);
        //执行任务
        for (int i = 0; i <10 ; i++) {
            service.execute(new Runnable() {
                @Override
                public void run() {
                    System.out.println("线程名:"+Thread.currentThread().getName());
                }
            });
        }

    }
}

在这里插入图片描述

在这种创建方式的基础上,创建了10个线程来执行2个任务,问当前创建了几个线程?
答:启动了两个线程。因为线程池创建线程是懒加载,再有任务的时候才会去创建线程,并不是new的时候就创建线程。

自定义线程池行为(设置线程池命名规则)

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory;

public class ThreadPoolDemo2 {

    public static void main(String[] args) {
        //线程工厂
        MyThreadFactory myThreadFactory = new MyThreadFactory();
       ExecutorService service =  Executors.newFixedThreadPool(10,myThreadFactory);
        for (int i = 0; i <10 ; i++) {
            service.execute(new Runnable() {
                @Override
                public void run() {
                    System.out.println("线程名:" + Thread.currentThread().getName()+"线程优先级" + Thread.currentThread().getPriority());
                }
            });
        }
    }
    private static int count;
    static class MyThreadFactory implements ThreadFactory{

        @Override
        public Thread newThread(Runnable r) {
            Thread thread = new Thread(r);
            //设置线程池的命名
            thread.setName("myThreadPoll-"+count++);
            //设置线程的优先级
            thread.setPriority(10);
            return thread;
        }
    }
}

在这里插入图片描述

第二种:创建带缓存的线程池(根据任务的数量生成对应的线程数,它适用于大量短期任务)

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class ThreadPoolDemo3 {
    public static void main(String[] args) {
        //创建带缓存的线程池
        ExecutorService service =  Executors.newCachedThreadPool();
        for (int i = 0; i <10 ; i++) {
            service.execute(new Runnable() {
                @Override
                public void run() {
                    System.out.println("线程名:"+Thread.currentThread().getName());
                }
            });
        }
    }
}

在这里插入图片描述

第三种:创建可以执行定时任务的线程池(三种方式)

import java.util.Date;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

public class ThreadPoolDemo4 {
    public static void main(String[] args) {
        //创建一个执行定时任务的线程池
        ScheduledExecutorService service =  Executors.newScheduledThreadPool(10);
        System.out.println("执行任务之前:"+new Date());
        //执行任务
        service.scheduleWithFixedDelay(new Runnable() {
            @Override
            public void run() {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("执行任务:"+new Date());
            }
        },1,3, TimeUnit.SECONDS);
    }
}

在这里插入图片描述
参数列表:

参数1:线程池的任务
参数2:定时任务延迟多长时间开始执行
参数3:定时执行任务的执行频率
参数4:配合参数2和3的时间单位

import java.util.Date;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

public class ThreadPoolDemo5 {
    public static void main(String[] args) {
        //创建一个执行定时任务的线程池
        ScheduledExecutorService service =  Executors.newScheduledThreadPool(10);
        System.out.println("执行任务之前:"+new Date());
        //执行任务
        service.schedule(new Runnable() {
            @Override
            public void run() {
                System.out.println("执行任务:"+new Date());
            }
        },3, TimeUnit.SECONDS);
    }
}

在这里插入图片描述
schedule相比于scheduleWithFixedDelay的区别:
1、没有延迟执行的时间设置。
2、定时任务只能执行一次。

import java.util.Date;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

public class ThreadPoolDemo6 {
    public static void main(String[] args) {
        //创建一个执行定时任务的线程池
        ScheduledExecutorService service =  Executors.newScheduledThreadPool(10);
        System.out.println("执行任务之前:"+new Date());
        //执行任务
        service.scheduleAtFixedRate(new Runnable() {
            @Override
            public void run() {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("执行任务:"+new Date());
            }
        },1,3, TimeUnit.SECONDS);
    }
}

在这里插入图片描述

scheduleWithFixedDelay与scheduleAtFixedRate的区别:
scheduleWithFixedDelay:执行任务的开始时间是以上次任务结束时间作为开始时间的(时间不固定,根据任务执行的时间来定)

scheduleAtFixedRate:开始时间是以上次任务的开始时间做起始时间(固定时间)

第四种:创建单个执行定时任务的线程池

import java.util.Date;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

public class ThreadPoolDemo7 {
    public static void main(String[] args) {
        //创建单个执行定时任务的线程池
        ScheduledExecutorService service =  Executors.newSingleThreadScheduledExecutor();
        service.scheduleAtFixedRate(new Runnable() {
            @Override
            public void run() {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("执行任务:"+new Date());
            }
        },1,3, TimeUnit.SECONDS);
    }
}

在这里插入图片描述
单个线程池有什么意义?
答:1、无需频繁的创建个销毁线程
2、可以更好地分配和管理以及存储任务(任务队列)

第五种:创建单个线程的线程池(频繁的创建和消耗、更好地分配和执行任务,并且可以将任务放到缓存队列中)

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class ThreadPoolDemo8 {
    public static void main(String[] args) {
        //创建单个线程的线程池
       ExecutorService service =  Executors.newSingleThreadExecutor();
       //执行任务
        for (int i = 0; i < 10; i++) {


        service.execute(new Runnable() {
            @Override
            public void run() {
                System.out.println("线程名:"+Thread.currentThread().getName());
            }
        });
        }
    }
}

在这里插入图片描述

第六种:(JDK8+)根据当前的工作环境(CPU核心数、任务量)创建异步线程池

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class ThreadPoolDemo9 {
    public static void main(String[] args) {
        //根据当前工作环境来创建线程
        ExecutorService service =  Executors.newWorkStealingPool();
        for (int i = 0; i <10 ; i++) {
            service.execute(new Runnable() {
                @Override
                public void run() {
                    System.out.println("线程名:"+Thread.currentThread().getName());
                }
            });
        }
        //等待异步线程池执行完成(根据线程池的终止状态来判断)
        while(!service.isTerminated()){

        }

    }
}

在这里插入图片描述
synchronize:同步
同步:按照某种规则按序执行的就叫同步。
同步执行流程:
1、main调用线程池
2、线程池执行完之后
3、关闭线程池、mian也会随之关闭

异步执行的流程:
1、main调用异步线程池
2、异步线程池后台执行,对于main来说来说,异步线程池已经执行完成,关闭main线程

Executors创建线程的问题:

1、线程数量不可控(线程的过度切换和争取-》程序执行比较慢)
2、任务数量不可控(任务数无线大 Integer.MAX_VALLUE),当任务量比较大的情况下就会造成内存异常(OOM)。

所以就有了第七种创建方式,也是最实用的创建方式

第七种:原始的创建线程池的方法

import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

public class ThreadPoolDemo10 {
    public static void main(String[] args) {
        //原始的创建线程池的方法
        ThreadPoolExecutor executor =
                new ThreadPoolExecutor(5,10,60, TimeUnit.SECONDS,
                        new LinkedBlockingQueue<>(1000));//任务队列一定要设置容量
        for (int i = 0; i < 5; i++) {
            executor.execute(new Runnable() {
                @Override
                public void run() {
                    System.out.println("线程名:"+Thread.currentThread().getName());
                }
            });
        }
    }
}

在这里插入图片描述

ThreadPoolExecutor 优点:

a、解决了线程数量不可控的问题 b、解决了任务数不可控问题

ThreadPoolExecutor 参数:

1、核心线程数(正式员工)
2、最大线程数(正式员工+临时工)
3、生存时间(long)
4、时间单位
5、任务队列
6、线程工厂(优先级、命名、类型。。。)
7、拒绝策略

这里重点说一下拒绝策略:

拒绝策略:

JDK4种策略+自定义策略
1、默认的拒绝策略:不执行任务,抛出异常

import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

public class ThreadPoolDemo13 {
    public static void main(String[] args) {
        //创建线程池
        ThreadPoolExecutor executor = new ThreadPoolExecutor(5,5,0, TimeUnit.SECONDS,
                new LinkedBlockingQueue<>(5),new ThreadPoolExecutor.AbortPolicy());
        for (int i = 0; i < 11; i++) {
            int finall = i;
            executor.execute(new Runnable() {
                @Override
                public void run() {
                    System.out.println("任务:"+finall+",线程名:"+Thread.currentThread().getName());
                }
            });
        }
    }
}

在这里插入图片描述
2、使用调用线程池的线程来执行任务:使用主线程来执行任务

import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

public class ThreadPoolDemo14 {
    public static void main(String[] args) {
        //创建线程池
        ThreadPoolExecutor executor = new ThreadPoolExecutor(5,5,0, TimeUnit.SECONDS,
                new LinkedBlockingQueue<>(5),new ThreadPoolExecutor.CallerRunsPolicy());
        for (int i = 0; i < 11; i++) {
            int finall = i;
            executor.execute(new Runnable() {
                @Override
                public void run() {
                    System.out.println("任务:"+finall+",线程名:"+Thread.currentThread().getName());
                }
            });
        }
    }
}

在这里插入图片描述
3、忽略(新)任务:这里的任务的新旧指的是在任务队列里得任务。

import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

public class ThreadPoolDemo15 {
    public static void main(String[] args) throws InterruptedException {
        //创建线程池
        ThreadPoolExecutor executor = new ThreadPoolExecutor(5,5,0, TimeUnit.SECONDS,
                new LinkedBlockingQueue<>(5),new ThreadPoolExecutor.DiscardPolicy());
        for (int i = 0; i < 11; i++) {
            int finall = i;
            executor.execute(new Runnable() {
                @Override
                public void run() {
                    System.out.println("任务:"+finall+",线程名:"+Thread.currentThread().getName());
                }
            });

        }
    }
}

在这里插入图片描述
这里可以看到没有执行任务10,任务时就是相对于任务队列里已经放满了的新任务。

4、忽略(老)任务:这里的任务的新旧指的是在任务队列里得任务。

import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

public class ThreadPoolDemo16 {
    public static void main(String[] args) throws InterruptedException {
        //创建线程池
        ThreadPoolExecutor executor = new ThreadPoolExecutor(5,5,0, TimeUnit.SECONDS,
                new LinkedBlockingQueue<>(5),new ThreadPoolExecutor.DiscardOldestPolicy());
        for (int i = 0; i < 11; i++) {
            int finall = i;
            executor.execute(new Runnable() {
                @Override
                public void run() {
                    System.out.println("任务:"+finall+",线程名:"+Thread.currentThread().getName());
                }
            });

        }
    }
}

在这里插入图片描述
这里可以看到没有执行任务5,因为任务5是加入任务队列的第一个任务,这里指的就是旧任务。

5、自定义策略:

import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.RejectedExecutionHandler;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

public class ThreadPoolDemo60 {
    public static void main(String[] args) throws InterruptedException {

        // 创建线程池
        ThreadPoolExecutor executor = new ThreadPoolExecutor(
                5, 5,
                0, TimeUnit.SECONDS,
                new LinkedBlockingQueue<>(5),
                new RejectedExecutionHandler() {
                    @Override
                    public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
                        // 自定义拒绝策略
                        System.out.println("执行了自定义拒绝策略");
                    }
                });
        for (int i = 0; i < 11; i++) {
            int finalI = i;
            executor.execute(new Runnable() {
                @Override
                public void run() {
                    System.out.println("任务:" + finalI + ",线程名:" +
                            Thread.currentThread().getName());
                }
            });
//            Thread.sleep(200);
        }


    }
}

线程池的优点:

1、避免频繁创建和消耗所带来的的性能开销
2、可以优化的拒绝任务
3、可以有更多的功能

线程池的两种执行方式:

1、execute(new Runnable)执行 无返回值得
2、submit 执行(new Runnable) 无返回值/ (new Callable) 有返回值
这里演示一下submit执行(new Callable):

import java.util.Random;
import java.util.concurrent.*;

public class ThreadPoolDemo17 {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        ThreadPoolExecutor executor = new ThreadPoolExecutor(5,5,0, TimeUnit.SECONDS,
                new LinkedBlockingQueue<>(1000));
        //返回返回值
        Future<Integer> future =  executor.submit(new Callable<Integer>() {

            @Override
            public Integer call() throws Exception {
                int num = new Random().nextInt(10);
                System.out.println("线程池生成了随机数" + num);
                return num;
            }
        });
        System.out.println("main得到返回值" + future.get());
    }
}

在这里插入图片描述
可以看到主线程拿到了返回值8.

线程池的特征:

线程池相比于线程来说是长生命周期,即使没有任务了,也会运行并等待任务。

线程池的关闭:

1、executor.shutdown():拒绝新任务加入,等待线程池中的任务队列执行完之后再停止线程池。 SHUTDOWN状态
2、executor.shutdownNow():拒绝新任务加入,不会等待任务队列中的任务完成就会停止线程池。STOP状态

import java.util.Random;
import java.util.concurrent.*;

public class ThreadPoolDemo18 {

    public static void main(String[] args) throws ExecutionException, InterruptedException {
        ThreadPoolExecutor executor = new ThreadPoolExecutor(5,5,0, TimeUnit.SECONDS,
                new LinkedBlockingQueue<>(1000));
        //返回返回值
        Future<Integer> future =  executor.submit(new Callable<Integer>() {

            @Override
            public Integer call() throws Exception {
                int num = new Random().nextInt(10);
                System.out.println("线程池生成了随机数" + num);
                return num;
            }
        });
        System.out.println("main得到返回值" + future.get());
        //关闭线程池
        executor.shutdown();
        //executor.shutdownNow();
    }
}

线程池状态(5种)

注意:线程池状态不等于线程的状态(6种)
1、RUNNING 运行状态
2、SHUTDOWN 执行executor.shutdown()后的状态。该状态下线程池不再接受新任务,但是会将工作队列中的任务执行结束。
3、STOP 执行executor.shutdownNow()后的状态。该状态下线程池不再接受新任务,但是不会处理工作队列中的任务。
4、TIDYING : 销毁前的前置状态
5、TERMINATED 销毁状态

注意:线程池的状态只是给开发者使用的,客户机是不可见的。

线程池的整体执行流程图

在这里插入图片描述

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

宋丹尼尔

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值