爬梯:JUC并发编程(二)

学习资源整理自:B站《狂神说》
书接上回

JUC并发编程

8、线程池(重点)

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

池化技术

程序的执行,本质:占用系统的资源!优化资源的使用==》池化技术

线程池、连接池、内存池、对象池…

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

线程池的好处

1、降低资源的消耗

2、提高响应的速度

3、方便管理

线程服用、可以管理最大并发数、管理线程

三大方法

newSingleThreadExecutor() 单线程线程池

private static void singlePool() {
    
    
        ExecutorService threadPool_single = Executors.newSingleThreadExecutor();//单线程 线程池
        //执行二十个任务
        for (int i = 0; i < 20; i++) {
    
    
            //使用线程池方式执行线程任务
            threadPool_single.execute(() -> {
    
    
                System.out.println(Thread.currentThread().getName() + " 石似心");
            });
        }
        //使用完之后需要关闭线程池
        threadPool_single.shutdown();
    }

控制台:

pool-1-thread-1 石似心
pool-1-thread-1 石似心
pool-1-thread-1 石似心
pool-1-thread-1 石似心
pool-1-thread-1 石似心
pool-1-thread-1 石似心
......

newFixedThreadExecutor() 固定大小线程池

public static void fixedPool(){
    
    
    ExecutorService threadPool_fixed = Executors.newFixedThreadPool(5);//固定大小 线程池
    //执行二十个任务
    for (int i = 0; i < 20; i++) {
    
    
        //使用线程池方式执行线程任务
        threadPool_fixed.execute(()->{
    
    
            System.out.println(Thread.currentThread().getName()+" 石似心");
        });
    }
    //使用完之后需要关闭线程池
    threadPool_fixed.shutdown();
}

控制台:

pool-1-thread-2 石似心
pool-1-thread-2 石似心
pool-1-thread-1 石似心
pool-1-thread-3 石似心
pool-1-thread-4 石似心
pool-1-thread-5 石似心
pool-1-thread-2 石似心
......

newCachedThreadExecutor() 弹性线程池

public static void cachedPool(){
    
    
    ExecutorService threadPool_cached = Executors.newCachedThreadPool();//弹性线程池,遇强则强
    //执行二十个任务
    for (int i = 0; i < 20; i++) {
    
    
        //使用线程池方式执行线程任务
        threadPool_cached.execute(()->{
    
    
            System.out.println(Thread.currentThread().getName()+" 石似心");
        });
    }
    //使用完之后需要关闭线程池
    threadPool_cached.shutdown();
}

控制台

pool-1-thread-8 石似心
pool-1-thread-6 石似心
pool-1-thread-7 石似心
pool-1-thread-4 石似心
pool-1-thread-3 石似心
pool-1-thread-4 石似心
pool-1-thread-10 石似心
......

七大参数

源码:

// 三大参数:
public static ExecutorService newSingleThreadExecutor() {
    
    
    return new FinalizableDelegatedExecutorService
        (new ThreadPoolExecutor(1, 1,
                                0L, TimeUnit.MILLISECONDS,
                                new LinkedBlockingQueue<Runnable>()));
}
public static ExecutorService newFixedThreadPool(int nThreads) {
    
    
    return new ThreadPoolExecutor(nThreads, nThreads,
                                  0L, TimeUnit.MILLISECONDS,
                                  new LinkedBlockingQueue<Runnable>());
}
public static ExecutorService newCachedThreadPool() {
    
    
    return new ThreadPoolExecutor(0, Integer.MAX_VALUE, // 21亿 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.acc = System.getSecurityManager() == null ?
        null :
    AccessController.getContext();
    this.corePoolSize = corePoolSize;
    this.maximumPoolSize = maximumPoolSize;
    this.workQueue = workQueue;
    this.keepAliveTime = unit.toNanos(keepAliveTime);
    this.threadFactory = threadFactory;
    this.handler = handler;
}

自定义线程池

阿里巴巴开发手册:

在这里插入图片描述

图解线程池:

在这里插入图片描述

代码实现:

public static void main(String[] args) {
    
    
    // 创建线程池,使用 ThreadPoolExecutor
    ExecutorService definedPool = new ThreadPoolExecutor(
            2,
            5,
            3,
            TimeUnit.SECONDS,
            new LinkedBlockingQueu在这里插入图片描述

e<>(3), // 排队人数
            Executors.defaultThreadFactory(),
            new ThreadPoolExecutor.AbortPolicy()); // 拒绝策略
    try {
    
    
        for (int i = 0; i < 5; i++) {
    
    
        definedPool.execute(()->{
    
    
            System.out.println(Thread.currentThread().getName() + " ok");
        });
        }
    } catch (Exception e) {
    
    
        e.printStackTrace();
    } finally {
    
    
        definedPool.shutdown();
    }
}

四种拒绝策略

四个ThreadPoolExecutor的内部类,实现了RejectedExecutionHandler接口

线程最大承受数:最大线程数 + 阻塞队列数

达到最大承受数后:

  • new ThreadPoolExecutor.CallerRunsPolicy()

    新来的任务会返回到创建线程池的线程执行,例如 main

  • new ThreadPoolExecutor.AbortPolicy()

    新来的任务不会有线程处理,抛出异常

  • new ThreadPoolExecutor.DiscardPolicy()

    新来的任务会被丢弃,不执行,不抛出异常

  • ThreadPoolExecutor.DiscardOldestPolicy()

    新来的任务会尝试使用最早的运行的线程来运行,若最早的线程恰好执行完任务,则执行新的任务,若最早的任务亦没有处理完任务,则新来的任务会被抛弃,不执行,不抛出异常

CPU密集型、IO密集型(调优)

最大线程数如何定义呢?

1、CPU密集型:多少核就是多少线程,保持CPU的效率最高

2、IO密集型:判断程序中较为消耗IO的线程:例如15个大型任务,io十分消耗资源,则定义为 16个即可。

//获取CPU数
Runtime.getRuntime().availableProcessors();

9、四大函数式接口(必须掌握)

新时代程序员:

lamdba表达式、链式编程、函数式接口、Stream流试计算

所有函数式接口都可以用lamdba表达式简化

函数式接口:只有一个方法的接口

@FunctionalInterface
public interface Runnable {
    
    
    public abstract void run();
}

四个函数式接口

在这里插入图片描述

Function 函数式接口

源码:

// T传入参数类型,R返回值类型

@FunctionalInterface
public interface Function<T, R> {
    
    
    R apply(T t);

代码实现:

public static void main(String args[]){
    
    
    // Function 函数式接口
    // 工具类:将Integer转为String
    Function<Integer,String> function = new Function<Integer,String>(){
    
    
        @Override
        public String apply(Integer i) {
    
    
            return String.valueOf(i);
        }
    };
    System.out.println(function.apply(123));
}

Predicate 断定型接口

源码:

//T传入参数类型,返回boolean

@FunctionalInterface
public interface Predicate<T> {
    
    
    boolean test(T t);

代码实现:

public static void main(String args[]){
    
    
    // 工具类:判断字符串是否等于 “周杰伦”
    // 使用lamdba简化
    Predicate<String> predicate = (str)->{
    
    return "周杰伦".equals(str);};
    //使用 predicate
    System.out.println(predicate.test("石似心"));
    //consle:false
}

Consumer 消费型接口

代码实现:

public static void main(String[] args) {
    
    
    //消费型接口 只有参数,没有返回值
    Consumer<String> consumer = (str)->{
    
    System.out.println("打印:"+str);};
    consumer.accept("石似心");
}

Supplier 供给型接口

代码实现:

public static void main(String args[]){
    
    
    // Supplier 供给型接口
    Supplier<String> supplier = new Supplier<String>() {
    
    
        @Override
        public String get() {
    
    
            return "1024";
        }
    };
    System.out.println(supplier.get());
}

10、Stream 流式计算

存储 + 计算

题目要求:一行代码实现

现有5个用户,筛选:

1、ID必须是偶数

2、年龄大于23岁

3、用户名转为大写

4、用户名倒序

5、只输出第一个用户

public static void main(String args[]){
    
    
     /**
      题目要求:一行代码实现
        现有5个用户,筛选:
        1、ID必须是偶数
        2、年龄大于23岁
        3、用户名转为大写
        4、用户名倒序
        5、只输出第一个用户
     */
     User user1 = new User(1,22,"a");
     User user2 = new User(2,23,"b");
     User user3 = new User(3,24,"c");
     User user4 = new User(4,25,"d");
     User user5 = new User(6,26,"e");
     // lamdba、链式编程、函数式接口、stream流式计算
     List<User> users = Arrays.asList(user1, user2, user3, user4, user5);
     users.stream()
             .filter(u->{
    
    return (u.getId()%2==0)&&(u.getAge()>23);})
             .map((u)->{
    
    u.setName(u.getName().toUpperCase());return u;})
             .sorted((u1,u2)->{
    
    return u2.getName().compareTo(u1.getName());})
             .limit(1)
             .forEach(System.out::println);
}

11、ForkJoin

概念:拆分合并

从JDK1.7开始,Java提供ForkJoin框架用于并行执行任务,它的思想就是讲一个大任务拆分成若干小任务,最终合并每个小任务的结果得到这个大任务的结果。

我的个人理解;是一个大型递归拆分任务,并且有专门线程池执行细小任务,然后逐层合并向上返回结果的框架。

ForkJoin 特点:工作窃取

当B线程率先完成了B任务队列的任务,就会去A线程中,窃取一个任务过来执行。

在这里插入图片描述

官方文档:

在这里插入图片描述
在这里插入图片描述

ForkJoinPool

ForkJoinTask:计算类需要继承分之合并任务类

RecursiveAction:递归事件类,没有返回值,只顾执行

RecursiveTask:递归任务类,有返回值

代码实现:

计算:从1加到10亿

任务类:

import java.util.concurrent.RecursiveTask;

/**
 * 任务类,继承 RecursiveTask
 * 将大任务 拆分!!!
 * @author: stone
 * @create: 2020-08-23 22:48
 */
public class DemoTask extends RecursiveTask<Long> {
    
    

    private long start;// 0
    private long end;// 1_0000_0000
    private long flag = 1000l;// 拆分点:每1000拆分出一个任务
    public DemoTask(long start, long end) {
    
    
        this.start = start;
        this.end = end;
    }
    /**
     * 实现RecursiveTask抽象方法
     * 执行具体业务的方法
     * @return
     */
    @Override
    protected Long compute() {
    
    
        Long resultLong=0l;
        //规划两条路线,当值在 1~1000内 时执行循环“加”
        if((end-start)<flag){
    
    
            //当前传进来的start到end数值距离小于1000
            //属于小任务
            for (long i = start; i <= end; i++) {
    
    
                resultLong += i;
            }
        }else{
    
    
            //任务还没达到拆分点,需要拆分
            //1、当前两个值去中间值 将任务对半分
            long middle = (start+end)/2;
            //2、创建任务类
            DemoTask demoTask1 = new DemoTask(start,middle);
            DemoTask demoTask2 = new DemoTask(middle+1, end);
            //3、将任务执行:使用fork() 将任务压入线程队列
            demoTask1.fork();
            demoTask2.fork();
            //4、获取子任务的执行结果,向上返回
            resultLong = demoTask1.join() + demoTask2.join();
        }
        return resultLong;
    }
}

执行任务类:

    /**
     * 使用forkJoin
     * @throws ExecutionException
     * @throws InterruptedException
     */
    static void userForkJoin() throws ExecutionException, InterruptedException {
    
    
        ForkJoinTask<Long> demoTask = new DemoTask(0l, 10_0000_0000l);
        //1、使用 ForkJoinPool执行 ForkJoinTask,
        // ForkJoinPool执行:execute()只执行;submit()有返回值;invoke()貌似使用于集合;
        ForkJoinPool forkJoinPool = new ForkJoinPool();
        ForkJoinTask<Long> result = forkJoinPool.submit(demoTask);
        System.out.println(result.get());// 阻塞主线程等待结果
        // console : 500000000500000000
    }

拓展:使用strem流计算从0加到10亿

JDK1.8的stream流并行计算,速度快十倍…

/**
 * 使用Stream并行流计算
 */
static void userStream(){
    
    
    long reduce = LongStream.rangeClosed(0l, 10_0000_0000l).parallel().reduce(0, Long::sum);
    System.out.println("结果:"+reduce);
    // console: 结果:500000000500000000
}

未完待续

猜你喜欢

转载自blog.csdn.net/qq845484236/article/details/108191108