线程池|多线程

发布时间 2023-09-11 18:47:02作者: 司丝思

什么是线程池?

线程的引入是因为进程的花销很大,线程相较于进程的花销少了很多,但是随着并发程度提高,对性能的要求也提高,频繁的创建线程,销毁线程的开销似乎也挺大的,这种情况下,要想提高效率,就可以使用线程池来降低创建线程销毁线程的开销。线程池就是事先将需要的线程创建好,放到“池”中,需要线程的时候,直接从“池”里拿,用完之后又放回去。从“池”里获取线程和用完放回池里这两个操作是比创建销毁线程更高效的。创建线程销毁线程是交给操作内核来做的,而线程池是可以用代码就可以实现的,不必交给操作内核。

Java标准库中的线程池

上述newFixedThreadPool中,“new”是方法名字的一部分,不是new关键字。

 这里是使用Executors类中的newFixedThreadPool静态方法直接构造出一个对象来,相当于是把new操作隐藏到这个方法里了。我们把使用某个类的某个静态方法直接构造出对象的方法称为“工厂方法”,提供“工厂方法”的类称为“工厂类”,使用了“工厂类”的代码认为是使用了“工厂模式 ”。

上述代码创建了10 个线程,接下来可以给线程派活了。线程池中有一个重要的方法sumbit,给线程池提交若干个任务。

public static void main(String[] args) {
        ExecutorService pool = Executors.newFixedThreadPool(10);
        for(int i = 0;i < 1000;i++){
            pool.submit(new Runnable() {
                @Override
                public void run() {
                    System.out.println("hello"+" "+i);
                }
            });
        }
    }

线程池的工作模式可以认为是每个线程都派发了任务,当线程执行完任务之后,又可以去领取任务,由于每个线程执行任务的时间都是差不多的,因此可以认为每个线程执行任务的数量都差不多。进一步可以认为任务就在队列里中排队,线程依次来取任务,取一个执行一个,执行完了之后又去取下一个任务,如此循环直到任务执行完。

上述代码还涉及到变量捕获问题,i是主线程里的局部变量,随着主线程的代码执行结束就销毁了,可能主线程的for已经执行完了,当前run的任务在线程里还没排到,i就被销毁了。

 run方法是属于Runnable,它的执行不是立刻马上,而是在队列中排到它了,就让对应的线程去执行。为了避免作用域的差异,可能run执行的时候,i已经销毁了,所以就有了变量捕获,就是让run方法将主线程上的i给拷贝一份在run的栈上。修改的代码如下:

public static void main(String[] args) {
        ExecutorService pool = Executors.newFixedThreadPool(10);
        for(int i = 0;i < 1000;i++){
            int n = i;
            pool.submit(new Runnable() {
                @Override
                public void run() {
                    System.out.println("hello"+" "+n);
                }
            });
        }
    }

Executors 提供了四个线程池:

1、newFixedThreadPool:创建固定大小的线程池,能够控制最大并发数,超出的线程会在LinkedBlockingQueue阻塞队列中等待;

2、newCAchedThredPool:创建线程数目动态增长的线程池;

3、newSingleThredPool:创建只有一个线程的线程池;

4、newSchdeuledThredPool:可以指定一段时间后执行命令,类似于定时器。

Executors本质上是ThreadPoolExecutor的类的封装。

ThreadPoolExecutor参数介绍

 

 corePoolSize表示核心线程数,当线程池创建的线程数小于核心线程数时,即便此时创建的线程数有空闲的,也会创建一个新的线程来执行任务,直到线程数大于等于核心线程数,再根据是否有空闲线程来决定要不要创建新的线程。

maximumPoolSize表示最大线程数,即允许创建的最大线程数。

keepAliveTime,当创建的线程数大于核心线程数的时候,当线程的空闲时间超过了keepAliveTime,则空闲的线程就会被销毁。

unit,线程存活时间的单位,从小到大:纳秒,微妙,毫秒,秒,分,小时,天。

workQueue,存储任务的阻塞队列。

threadFactory,线程工厂,负责创建新的线程(提供线程的工厂)。

handler,线程饱和策略,当线程池和队列都满了,则表示线程池已经饱和。