线程池,,

线程池,提前把需要用的线程创建好放到线程池中,后面需要使用的时候,直接从池中获取,如果用完了,就还给线程池,此时创建线程/销毁线程的效率就提高了。用于优化频繁创建和销毁线程的场景。可以减少每次因为创建/销毁线程造成的损耗.

为什么从池子取的效率比新创建线程效率更高???
从池子取,这个动作,是纯粹用户态的操作.
创建新的线程,这个动作,则是需要 用户态 + 内核态 相互配合,完成的操作.
如果一段程序,是在系统内核中执行,此时就称为“内核态如果不是,则称为“用户态;

用户态程序的执行更为可控,想要执行某个任务(从线程池取线程,还线程)会很快的就完成了,

如果通过操作系统内核创建线程,销毁线程,需要通过系统调用,让内核来执行,但是内核身上背负的是整个计算机的活动,服务的是所有应用程序,整体过程是"不可控的",因此使用线程池更为高效。

Java提供了一个内置的线程池框架java.util.concurrent.ExecutorService,和并发编程息息相关,通过它来创建和管理线程池。 

ExecutorService executor = Executors.newFixedThreadPool(nThreads);

可以看出来这里不是new ExecutorService.涉及到工厂模式。通常创建对象,使用new,会触发类的构造方法,但是构造方法存在局限性,工厂模式就弥补这个局限性。

工厂模式:使用普通的方法来代替构造方法,创建对象(构造方法只能构造一种对象,如果要构造不同情况的对象,就很难了)

class Point{
    public Point(double x,double y) {
    }
    public Point(double r,double a) {
    }
}

编译器报错,正常来说,多个构造方法是"重载"的方式来提供的,重载要求:方法名相同,参数的个数或者类型不相同,为了解决这个问题,可以使用工厂模式.使用普通的方法,代替构造方法完成初始化工作,普通方法就可以使用方法的名字来区分了

2public class Point {
    private double x;
    private double y;

    public Point(double x, double y) {
        this.x = x;
        this.y = y;
    }

    public Point(double r, double a) {
        double x = r * Math.cos(Math.toRadians(a));
        double y = r * Math.sin(Math.toRadians(a));
        this.x = x;
        this.y = y;
    }

    public double getX() {
        return x;
    }

    public double getY() {
        return y;
    }
}

public interface PointFactory {
    Point createCartesianPoint(double x, double y);
    Point createPolarPoint(double r, double a);
}

public class DefaultPointFactory implements PointFactory {
    @Override
    public Point createCartesianPoint(double x, double y) {
        return new Point(x, y);
    }

    @Override
    public Point createPolarPoint(double r, double a) {
        return new Point(r * Math.cos(Math.toRadians(a)), r * Math.sin(Math.toRadians(a)));
    }
}
PointFactory cartesianFactory = new CartesianPointFactory();
ExecutorService executor = Executors.newFixedThreadPool(nThreads);
Executor executor= Executors.newCachedThreadPool();工厂类.工厂方法;

此时构造出来的线程池对象得线程数目可以自适应

public class demo6 {
    public static void main(String[] args) {
        ExecutorService service= Executors.newCachedThreadPool();
        service.submit(new Runnable(){
            @Override
            public void run() {
                System.out.println("hello,");
            }
        });
    }
}//ThreadPoolExecutor-g构造方法,注册方法

int corePoolSize核心线程数

int maximumPoolSize最大线程数

这个线程池里线程的数目是可以动态变化的变化范围就是 [corePooSize,maximumPoolSizel]

ThreadPoolExexutor相当于把里面的线程分为两类

一类是正式工(核心线程数),一类是临时工,两个加起来就是最大行程数

允许正式员工摸鱼,临时工不能摸鱼,临时工摸鱼就会被开除(销毁)

如果任务很多,那么我们就要更多的线程来完成,但一个程序的任务有时多有时少,少时我们需要对现有的线程进行一定的销毁..原则时正式工(核心线程)留着,临时工动态调节!实际开发时线程池中的线程设置为多少(N(cpu核数),N+1,1.5N,2N....)是不准确的,面试遇到,回答具体数字那么肯定是答错了..

对不同的程序,需要设置的线程数也是不同的

一个线程,执行的代码,主要有两类:
1.cpu 密集型: 代码里主要的逻辑是在进行 算术运算/逻辑判断

2.io 密集型: 代码里主要进行的是 io操作.
假设一个线程的所有代码都是 cpu 密集型代码,这个时候,线程池的数量不应该超过 N(设置 N 就是极限了)设置的比 N 更大,这个时候,也无法提高效率了.(cpu 吃满了)此时更多的线程反而增加调度的开销.
假设一个线程的所有代码都是 10 密集的,这个时候不吃 CPU,此时设置的线程数,就可以是超过 N.较大的值一个核心可以通过调度的方式,来并发执行嘛~~

代码不同, 线程池的线程数目设置就不同,无法知道一个代码,具体多少内容是 cpu 密集, 多少内容是 10 密集.
正确做法: 使用实验的方式, 对程序进行性能测试,测试过程中尝试修改不同的线程池的线程数目看哪种情况下,最符合要求

long keepAliveTime, TimeUnit unit,
这两个参数描述了临时工摸鱼的最大时间...如果超过这个时间,线程就被销毁了!

BlockingQueue<Runnable> workQueue
这个是线程池的任务队列
.此处使用阻塞队列,若没有任务,就阻塞,有就take成功...需要优先级就设置PriorityBlockingQueue,不想用优先级,任务数目恒定,使用ArrayBlockingQueue,不用优先级,任务数目变动大,使用LinkedBlockingQueue

ThreadFactory threadFactory是工厂模式的体现
用于线程池创建线程,主要是为了在创建过程中,对线程属性做出一些设置。

 RejectedExecutionHandler handler
描述了线程池的"拒绝策略",也是一个特殊的对象,描述了当线程池任务队列满了,如果继续添加任务会有什么行为。

第一种:如果任务多,队列满了,直接抛出异常

第二种:如果队列满了,多出来的任务,谁添加的谁负责执行

第三种:如果队列满了,丢弃任务队列最早的任务

第四种:丢弃最新的任务

创建阻塞队列,submit方法把任务添加到队列,使用拒绝策略为阻塞,创建线程执行任务 

import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
 
public class Test {
    public static void main(String[] args) throws InterruptedException {
        MyThreadPool myThreadPool = new MyThreadPool(10);
        for (int i = 0; i < 1000; i++) {
            int n = i;
            myThreadPool.submit(new Runnable() {
                @Override
                public void run() {
                    System.out.println(n);
                }
            });
        }
    }
}
class MyThreadPool{

    private BlockingQueue<Runnable> queue = new LinkedBlockingQueue<>();
    //n表示线程数
    public MyThreadPool(int n){
        for (int i = 0; i < n; i++) {
            Thread t = new Thread(()->{
               while(true){
                   try {
                       Runnable runnable = queue.take();
                       runnable.run();
                   } catch (InterruptedException e) {
                       throw new RuntimeException(e);
                   }
               }
            });
            t.start();
        }
    }
    //注册任务线程池
    public void submit(Runnable runnable) throws InterruptedException {
        queue.put(runnable);
    }
}

  • 14
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

sqyaa.

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

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

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

打赏作者

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

抵扣说明:

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

余额充值