JUC并发编程学习笔记(十)线程池(重点)

这篇具有很好参考价值的文章主要介绍了JUC并发编程学习笔记(十)线程池(重点)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

线程池(重点)

线程池:三大方法、七大参数、四种拒绝策略

池化技术

程序的运行,本质:占用系统的资源!优化资源的使用!-> 池化技术(线程池、连接池、对象池......);创建和销毁十分消耗资源

池化技术:事先准备好一些资源,有人要用就拿,拿完用完还给我。

线程池的好处:

1、降低资源消耗

2、提高相应速度

3、方便管理

线程复用、可以控制最大并发数、管理线程

线程池:三大方法

JUC并发编程学习笔记(十)线程池(重点)

1、newSingleThreadExecutor

单列线程池,只有一条线程;

单例线程池配合callable使用,注意需要在程序运行结束后关闭线程池

package org.example.pool;

import java.util.concurrent.*;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

//Executors->工具类
public class Demo01 {
    public static void main(String[] args) {
        TestCallable able = new TestCallable();

        //三大方法
        ExecutorService threadPool = Executors.newSingleThreadExecutor();//单个线程


        try {
            for (int i = 0; i < 10; i++) {
                FutureTask<String> task = new FutureTask<String>(able);

                //线程池创建线程
                threadPool.execute(task);
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            //线程池用完,程序结束,关闭线程池
            threadPool.shutdown();
        }


//        Executors.newFixedThreadPool(5);//固定线程池大小
//        Executors.newCachedThreadPool();//可伸缩的线程池
    }
}

class TestCallable implements Callable<String> {
    Lock lock = new ReentrantLock();

    @Override
    public String call() throws Exception {
        TimeUnit.SECONDS.sleep(2);
        System.out.println(Thread.currentThread().getName() + ":ok");
        return Thread.currentThread().getName() + ":ok";
    }
}

注意结果图

JUC并发编程学习笔记(十)线程池(重点)

在单例线程池中,运行结果发现都是同一条线程在操作资源,整个线程池中只有一条pool-1-thread-1线程。

2、newFixedThreadPool

同样的代码,将线程池切换为固定大小的线程池,设置为5条,这样跑出来的结果又不一样

JUC并发编程学习笔记(十)线程池(重点)

由于设置了线程休眠,所以会导致比较平均的结果出现,但是一般情况下都是五条线程抢占资源,每次结果都是不一定的,看那条线程处理的比较快抢占的比较多。

3、newCachedThreadPool

同样的代码,将线程池切换为可伸缩大小的线程池,这样跑出来的结果又不一样

image-20231007193007427

根据业务代码生成具体条数的线程:如本次业务通过循环或其他因数,同时需要处理10条任务,那么当你线程池中的第一条线程还未完成任务时就会生成一条新的线程来同步处理这些任务,只要你cpu处理速度够快那么理论最高可能同时生成一个具有10条线程(任务数)的一个线程池。

七大参数

源码分析

public static ExecutorService newSingleThreadExecutor() {
    return new FinalizableDelegatedExecutorService
        (new ThreadPoolExecutor(1, 1,
                                0L, TimeUnit.MILLISECONDS,
                                new LinkedBlockingQueue<Runnable>()));
}
public static ExecutorService newFixedThreadPool(int nThreads) {
    //和以上没有区别,只是通过用户调用来完成的,相当于new ThreadPoolExecutor(5, 5,....)
    return new ThreadPoolExecutor(nThreads, nThreads,
                                  0L, TimeUnit.MILLISECONDS,
                                  new LinkedBlockingQueue<Runnable>());
}
public static ExecutorService newCachedThreadPool() {
    //设置了默认是0个线程,但是最大值可以达到大约21亿条,设置了Integer.MAX_VALUE(约21亿)
    return new ThreadPoolExecutor(0, Integer.MAX_VALUE,//如果通过Integer.MAX_VALUE来跑线程池一定会照成OOM(溢出)
                                  60L, TimeUnit.SECONDS,
                                  new SynchronousQueue<Runnable>());
}

通过观察以上三大方法的创建线程池方式,可以发现,三大方法的本质都是调用ThreadPoolExecutor来创建的

public ThreadPoolExecutor(int corePoolSize,//核心线程池大小
                          int maximumPoolSize,//最大线程池大小
                          long keepAliveTime,//超时无调用释放
                          TimeUnit unit,//超时单位
                          BlockingQueue<Runnable> workQueue,//阻塞队列
                          ThreadFactory threadFactory,//线程工厂,创建线程的,一般不用动
                          RejectedExecutionHandler handler) {//拒绝策略
    if (corePoolSize < 0 ||
        maximumPoolSize <= 0 ||
        maximumPoolSize < corePoolSize ||
        keepAliveTime < 0)
        throw new IllegalArgumentException();
    if (workQueue == null || threadFactory == null || handler == null)
        throw new NullPointerException();
    this.corePoolSize = corePoolSize;
    this.maximumPoolSize = maximumPoolSize;
    this.workQueue = workQueue;
    this.keepAliveTime = unit.toNanos(keepAliveTime);
    this.threadFactory = threadFactory;
    this.handler = handler;
}

这就是为什么阿里巴巴开发规范中说不要使用Executors来创建线程池而是让我们通过ThreadPoolExecutor来创建,其实就是让我们通过了解线程池的本质来避免一些问题。

JUC并发编程学习笔记(十)线程池(重点)

模拟银行业务模块模拟

package org.example.pool;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

public class Demo02 {
    public static void main(String[] args) {
        //自定义线程池,工作中只会使用ThreadPoolExecutor
        ThreadPoolExecutor threadPool = new ThreadPoolExecutor(2,
                5,
                3,
                TimeUnit.SECONDS,
                new LinkedBlockingQueue<>(3),
                new ThreadPoolExecutor.AbortPolicy());//拒绝策略,即当阻塞队列和线程池线程都已经最大化运行没有任何位置可以处置接下来的元素了,就拒绝该元素进入并抛出异常
        try {
            //最大承载 队列Queue+max
            for (int i = 0; i < 10; i++) {
                //线程池创建线程
                threadPool.execute(()->{
                    System.out.println(Thread.currentThread().getName() + ":ok");
                });
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            //线程池用完,程序结束,关闭线程池
            threadPool.shutdown();
        }
    }
}

此时有10个任务,但是最大线程量只有5个,阻塞队列量只有三个,所以注定会有两个无法处理,此时触发拒绝策略,抛出异常并且拒绝该任务。

JUC并发编程学习笔记(十)线程池(重点)

可以看到执行了八个任务,通过五条不同的线程。执行了拒绝策略抛出了异常java.util.concurrent.RejectedExecutionException

四种拒绝策略

JUC并发编程学习笔记(十)线程池(重点)

四种拒绝策略的描述

//AbortPolicy 队列满了,丢掉任务,抛出异常
//CallerRunsPolicy 哪条线程给的任务回到哪条线程去执行,线程池不执行
//DiscardPolicy 队列满了,丢掉任务,但不抛出异常
//DiscardOldestPolicy 队列满了,尝试去和最早的竞争,如果没成功,依旧丢弃任务,但不抛出异常

小结和拓展

了解:IO密集型、cpu密集型(调优)文章来源地址https://www.toymoban.com/news/detail-741807.html

/*
* 最大线程到底该如何定义
* 1、CPU 密集型,几何就定义为几,就可以保证cpu效率最高的
* 2、IO 密集型 > 判断你程序中十分耗IO的线程,
* 程序    15个大小任务 io十分占用资源
* */

到了这里,关于JUC并发编程学习笔记(十)线程池(重点)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • JUC并发编程学习笔记(十六)Volatile

    保证可见性 使用了volatile,即可保证它本身可被其他线程的工作内存感知,即变化时也会被同步变化。 不保证原子性 原子性:不可分割 线程A在执行任务时是不可被打扰的,也不能被分割,要么同时成功,要么同时失败。 每次结果也不一样。 如果不加Lock加synchronize

    2024年02月05日
    浏览(88)
  • JUC并发编程学习笔记(十四)异步回调

    Future设计的初衷:对将来的某个事件的结果进行建模 在Future类的子类中可以找到CompletableFuture,在介绍中可以看到这是为非异步的请求使用一些异步的方法来处理 点进具体实现类中,查看方法,可以看到CompletableFuture中的异步内部类,里面是实现的异步方法 以及一些异步方法

    2024年02月05日
    浏览(33)
  • JUC并发编程学习笔记(八)读写锁

    ReadWriteLock ReadWriteLock只存在一个实现类那就是ReentrantReadWriteLock,他可以对锁实现更加细粒化的控制 读的时候可以有多个阅读器线程同时参与,写的时候只希望写入线程是独占的 Demo:

    2024年02月06日
    浏览(35)
  • JUC并发编程学习笔记(十九)原子引用

    带版本号的原子操作! 解决ABA问题,引入原子引用(乐观锁思想) AtomicStampedReference类解决ABA问题 所有相同类型的包装类对象之间值的比较全部使用equals方法比较 Integer使用了对象缓存机制,默认范围是-128至127,推荐使用静态工厂方法valueOf获取对象实例,而不是new,因为v

    2024年02月05日
    浏览(41)
  • JUC并发编程学习笔记(四)8锁现象

    八锁-就是关于锁的八个问题 锁是什么,如何判断锁的是谁 对象、class模板 深刻理解锁 锁的东西无外乎就两样:1、同步方法的调用者,2、Class模板。 同一个锁中,只有当前线程资源释放后才会被下一个线程所接手。 同步方法的调用者是两个不同的实例时,互不相关。 静态

    2024年02月06日
    浏览(43)
  • JUC并发编程学习笔记(六)Callable(简单)

    callable接口和runnable接口类似,都是为了执行另外一条线程而设计的,区别是Runnable不会返回结果也不会抛出异常。 1、可以有返回值 2、可以抛出异常 3、方法不同;run()/call(); Runnable 实现Runnable接口,重写run方法,无返回值 Callable 实现Callable接口,重写call方法,有返回值,可

    2024年02月06日
    浏览(35)
  • JUC并发编程学习笔记(七)常用的辅助类

    CountDownLatch 这是一个JUC计数器辅助类,计数器有加有减,这是减。 使用方法 使用前 可能会在所有人没出去之前关门 使用后 不在乎谁先出去,但是一定要总数等于0后才会关门 原理 countDownLatch.countDown();//总数减1 countDownLatch.await();//等待总数变为0才会往下执行,相当于阻塞当

    2024年02月06日
    浏览(32)
  • JUC并发编程学习笔记(十八)深入理解CAS

    什么是CAS 为什么要学CAS:大厂你必须深入研究底层!有所突破! java层面的cas-------compareAndSet compareAndSet(int expectedValue, int newValue) 期望并更新,达到期望值就更新、否则就不更新! Unsafe类 java不能直接操作内存,但是可以调用c++,c++可以操作内存,java可以通过native定义

    2024年02月05日
    浏览(48)
  • JUC并发编程学习笔记(十二)Stream流式计算

    什么是Stream流式计算 大数据:存储+计算 集合、MySql这些的本质都是存储东西的; 计算都应该交给流来操作! 一个案例说明:函数式接口、lambda表达式、链式编程、Stream流式计算

    2024年02月05日
    浏览(36)
  • JUC并发编程学习笔记(十七)彻底玩转单例模式

    单例中最重要的思想-------构造器私有! 恶汉式、懒汉式(DCL懒汉式!) 恶汉式 懒汉式 DCL懒汉式 完整的双重检测锁模式的单例、懒汉式、DCL懒汉式 但是有反射!只要有反射,任何的代码都不安全,任何的私有都是摆设 正常的单例模式: 反射破坏单例: 怎么去解决这

    2024年02月05日
    浏览(33)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包