• java多线程(一)


    初始化线程的四种方式

    1、继承Thread

    1. public static void main(String[] args) {
    2.       System.out.println("main .... statt");
    3.       new Thread01().start();
    4.       System.out.println("main ... end");
    5.   }
    6.   public static class Thread01 extends Thread{
    7.       @Override
    8.       public void run() {
    9.           System.out.println("当前线程:"+Thread.currentThread().getId());
    10.           int i = 10 / 2;
    11.           System.out.println("运行结果:"+ i);
    12.       }
    13.   }

    运行结果 

     

    2、实现Runnable接口

    1. public static class Runable01 implements Runnable{
    2. @Override
    3. public void run() {
    4. System.out.println("当前线程:"+Thread.currentThread().getId());
    5. int i = 10 / 2;
    6. System.out.println("运行结果:"+ i);
    7. }
    8. }

     

    1. public static void main(String[] args) {
    2. System.out.println("main .... statt");
    3. //new Thread01().start();
    4. Runable01 runable01 = new Runable01();
    5. new Thread(runable01).start();
    6. System.out.println("main ... end");
    7. }

     

    3、实现Callable+futureTask

    该方法是可以获取线程执行结果的。

    1. public static class Callable01 implements Callable<Integer> {
    2. @Override
    3. public Integer call() throws Exception {
    4. System.out.println("当前线程:"+Thread.currentThread().getId());
    5. int i = 10 / 2;
    6. System.out.println("运行结果:"+ i);
    7. return i;
    8. }
    9. }
    1. public static void main(String[] args) {
    2. System.out.println("main .... start");
    3. //new Thread01().start();
    4. //Runable01 runable01 = new Runable01();
    5. //new Thread(runable01).start();
    6. Callable01 callable01 = new Callable01();
    7. FutureTask task = new FutureTask(callable01);
    8. new Thread(task).start();
    9. System.out.println("main ... end");
    10. }

     但是,使用Callable是可以获取线程的返回结果的。

    1. public static void main(String[] args) throws ExecutionException, InterruptedException {
    2. System.out.println("main .... start");
    3. //new Thread01().start();
    4. //Runable01 runable01 = new Runable01();
    5. //new Thread(runable01).start();
    6. Callable01 callable01 = new Callable01();
    7. FutureTask<Integer> task = new FutureTask<Integer>(callable01);
    8. new Thread(task).start();
    9. Integer integer = task.get();
    10. System.out.println("返回结果为===="+integer);
    11. System.out.println("main ... end");
    12. }

    执行结果:

     虽然有了异步线程,但是代码还是从上往下执行,原因就是线程调用了fuutetask的get方法是阻塞式等待。

    4、线程池

    为什么要用线程池?

    如果我们后来的业务逻辑非常多,都写我们的原生代码,new Thread().start();问题是非常大的。这就代表我们公司有新任务,就招聘一名员工。任务多了之后,最终会将资源(内存,堆,栈)耗尽。

    我们以后在业务代码里边,以上三种启动线程的方式我们都不用。应该将所有的多线程异步任务,都交给线程池来执行处理。(公司就50个员工,以后什么活就分配给这50个员工中的某一个,当然如果这个人有活,就等他处理完了,再处理新任务。达到了资源控制。)

    1、降低资源的消耗

    通过重复利用已经创建好的线程降低线程创建和销毁带来的损耗;

    2、提高响应速度

    因为线程池中的线程数没有超过线程池的最大上限时,有的线程处于等待分配的状态,当任务来的时候,无需创建新的线程就能执行,节省了线程创建的时间。

    3、提高线程的可管理性

    线程池会根据当前系统特点,对池内的线程进行优化处理,减少创建和销毁带来的系统开销。无限的创建和销毁不仅消耗系统资源,还降低系统的稳定性。

    JUC里边有个最快的方式得到线程池,

    ExecutorService pool = Executors.newFixedThreadPool(10);

    当然,这个线程池应该是整个系统一个线程池,而不是每次都创建一个线程池。

    线程池执行的方法调用如下:

    1. // pool.submit(new Runable01());
    2. pool.execute(new Runable01());

     

    区别如下:

    execute的方法类型是void。submit有返回结果。

    返回结果:

    线程池的创建方式

    1、Executors类

    2、原生线程池

    1. ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor();
    2. //JDK源码
    3. public ThreadPoolExecutor(int corePoolSize, //核心线程数[一直存在,除非设置了核心线程超时销毁的属性-allowCoreThreadTimeOut]:线程池创建好以后,就准备就绪的线程数量,等待接收异步任务去执行。只是new了几个Thread,但是并没有start();
    4. int maximumPoolSize, //线程池的最大线程数量,控制资源并发。
    5. long keepAliveTime, //存活时间:当我们线程数量大于核心线程数量的时候,只有线程空闲大于指定的最大存活时间,非核心线程释放。
    6. TimeUnit unit,
    7. BlockingQueue workQueue,//阻塞队列,如果任务有很多,把(总的异步任务数量-最大线程数量),放入到队列里边。只要线程空闲了,就去队列里边取出新的任务,继续执行。
    8. ThreadFactory threadFactory,//创建线程的工厂,默认的。
    9. RejectedExecutionHandler handler) { //如果队列满了,按照指定的拒绝策略,拒绝执行我们的任务。
    10. if (corePoolSize < 0 ||
    11. maximumPoolSize <= 0 ||
    12. maximumPoolSize < corePoolSize ||
    13. keepAliveTime < 0)
    14. throw new IllegalArgumentException();
    15. if (workQueue == null || threadFactory == null || handler == null)
    16. throw new NullPointerException();
    17. this.corePoolSize = corePoolSize;
    18. this.maximumPoolSize = maximumPoolSize;
    19. this.workQueue = workQueue;
    20. this.keepAliveTime = unit.toNanos(keepAliveTime);
    21. this.threadFactory = threadFactory;
    22. this.handler = handler;
    23. }

     

    线程池的工作顺序

    1、线程池创建好,准备好core数量的核心线程,准备接受任务。

    2、 新的任务进来,用core准备好空闲线程执行。

    (1)core满了,就将新进来的线程放入到阻塞队列中去,空闲的core就会自己去阻塞队列获取任务执行。

    (2)阻塞队列满了,就直接开新线程执行,最大只能开到max的执行数量。

    (3)max都执行好了,max-core数量空闲的线程会在keepAliveTime指定的时间后,自动销毁,最终保持到core大小。

    (4)如果线程开到了max的数量,还有新任务进来,就会使用reject指定的拒绝策略进行处理。

    1、可以丢弃

    2、可以同步执行。new thread().start()是异步的。

     

    AbortPolicy:抛弃策略;抛出异常;

    DiscartPolicy:抛弃策略,不抛出异常;

     

    CallerRunsPlicy:的run()是同步执行的,只有new Thread().start()才是异步执行的。

    3、所有的线程都是由指定的factory创建的。

    1. ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
    2. 5,
    3. 200,
    4. 10,
    5. TimeUnit.SECONDS,
    6. new LinkedBlockingDeque<>(10000),//不指定的话,默认是Integer的最大值。一直占用内存。
    7. Executors.defaultThreadFactory(),
    8. new ThreadPoolExecutor.AbortPolicy()//丢弃策略
    9. );

     

    线程池的种类

    1. Executors.newCachedThreadPool();//核心线程数量是0个,但是最大线程数很大,60秒不用就回收
    2. public static ExecutorService newCachedThreadPool() {
    3. return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
    4. 60L, TimeUnit.SECONDS,
    5. new SynchronousQueue());
    6. }
    7. Executors.newFixedThreadPool();//最大线程数=核心线程数,核心线程从阻塞队列中取任务。阻塞队列满了不会创建非核心线程。
    8. public static ExecutorService newFixedThreadPool(int nThreads) {
    9. return new ThreadPoolExecutor(nThreads, nThreads,
    10. 0L, TimeUnit.MILLISECONDS,
    11. new LinkedBlockingQueue());
    12. }
    13. Executors.newScheduledThreadPool(); //可以存放定时延期执行的任务线程
    14. public ScheduledThreadPoolExecutor(int corePoolSize) {
    15. super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
    16. new DelayedWorkQueue());
    17. }
    18. Executors.newSingleThreadExecutor() //核心线程=最大线程=1,线程执行完毕一个,就从阻塞队列中获取一个任务。
    19. public static ExecutorService newSingleThreadExecutor() {
    20. return new FinalizableDelegatedExecutorService
    21. (new ThreadPoolExecutor(1, 1,
    22. 0L, TimeUnit.MILLISECONDS,
    23. new LinkedBlockingQueue()));
    24. }

    总结

    由此可见,方式1和方式2,都是异步的,主线程是无法获取到其他线程的结果的。

    FutureTask不仅可以接收callable参数,Runable参数也是可以接收的。

    JDK源码如下:

    1. public FutureTask(Callable callable) {
    2. if (callable == null)
    3. throw new NullPointerException();
    4. this.callable = callable;
    5. this.state = NEW; // ensure visibility of callable
    6. }
    7. public FutureTask(Runnable runnable, V result) {
    8. this.callable = Executors.callable(runnable, result);
    9. this.state = NEW; // ensure visibility of callable
    10. }

    runable方法配合futureTask实现返回值也是可以的,把返回值配置到result对象里边即可。

  • 相关阅读:
    7种方式企业内部资料共享,你pick谁?
    TPAMI 2022 | 自动搜索文本识别网络的高性能特征提取器
    kubernetes scheduler初识
    强化学习输入数据归一化(标准化)
    Java之线程池的详细解析
    Java类的继承
    python基础练习题库实验1
    TiDB表库过滤
    单片机练习题3
    1. 约瑟夫问题
  • 原文地址:https://blog.csdn.net/pshdhx/article/details/126319143