欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

线程池

程序员文章站 2022-05-06 07:52:22
...

 

使用线程池的目的

(1)线程时稀缺资源,不能频繁创建

(2)解耦,把运行和创建销毁线程分离开了

(3)线程可以复用

线程池原理

核心思想就是把宝贵的资源放到一个池子里,每次使用都从里面获取,用完之后放回池子*其他人使用

 Executors 的调用线程池的方法

(1)线程池的分类

static ExecutorService newFixedThreadPool(int nThreads)

创建一个线程池,该线程池重用固定数量的从共享*队列中运行的线程。

static ExecutorService newCachedThreadPool()     (可变大小线程池)

创建一个根据需要创建新线程的线程池,但在可用时将重新使用以前构造的线程。

static ExecutorService newSingleThreadExecutor()   (减少线程的创建和销毁的开销,实现了重用性)

创建一个使用从*队列运行的单个工作线程的执行程序。

static ScheduledExecutorService newScheduledThreadPool(int corePoolSize)

创建一个线程池,可以调度命令在给定的延迟之后运行,或定期执

(2)使用

定义一个线程

public class TaskDemo implements Runnable{
    @Override
    public void run() {
        try {
            TimeUnit.SECONDS.sleep(2);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName()+" is running");
    }
}

定义四种线程池

FixedPoolDemo

public class FixedPoolDemo {
    public static void main(String[] args) {
        //创建固定大小的线程池
        ExecutorService pool = Executors.newFixedThreadPool(5);
        //创建十个任务给线程池
        for (int i = 0; i < 10; i++) {
            TaskDemo task = new TaskDemo();
            //把任务交给pool去执行
            pool.execute(task);
        }
        //关闭线程池
        pool.shutdown();
    }


}

 CachePool

//可变大小的
public class CachePool {
    public static void main(String[] args) {
        ExecutorService pool = Executors.newCachedThreadPool();
        for (int i = 0; i < 10; i++) {
            TaskDemo task = new TaskDemo();
            //把任务交给pool去执行
            pool.execute(task);

        }
    }
}

SingletonPool

public class SingletonPool {
    public static void main(String[] args) {
        ExecutorService pool = Executors.newSingleThreadExecutor();
        //创建十个任务给线程池
        for (int i = 0; i < 10; i++) {
            TaskDemo task = new TaskDemo();
            //把任务交给pool去执行
            pool.execute(task);
        }
        //关闭线程池
        pool.shutdown();
    }
}

 ScheduledPool

//可调度的
public class ScheduledPool {
    public static void main(String[] args) {
        ScheduledExecutorService pool = Executors.newScheduledThreadPool(5);
        for (int i = 0; i < 10; i++) {
            TaskDemo task = new TaskDemo();
            //把任务交给pool去执行
            pool.execute(task);
        }
        //关闭线程池
        pool.shutdown();
    }
}

 

原理

Executors方法源码

public static ExecutorService newFixedThreadPool(int nThreads) {
        return new ThreadPoolExecutor(nThreads, nThreads,
                                      0L, TimeUnit.MILLISECONDS,
                                      new LinkedBlockingQueue<Runnable>());
    }
 public static ExecutorService newSingleThreadExecutor() {
        return new FinalizableDelegatedExecutorService
            (new ThreadPoolExecutor(1, 1,
                                    0L, TimeUnit.MILLISECONDS,
                                    new LinkedBlockingQueue<Runnable>()));
    }
  public static ExecutorService newCachedThreadPool() {
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                      60L, TimeUnit.SECONDS,
                                      new SynchronousQueue<Runnable>());
    }

 newSingleThreadExecutor  newSingleThreadExecutor   newFixedThreadPool 调用的都是同一个方法new ThreadPoolExecutor()根据参数的不同来判断不同的线程池方法

 public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue) {
        this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
             Executors.defaultThreadFactory(), defaultHandler);
    }

核心参数  

 int corePoolSize  : 核心线程池的基本大小
  int maximumPoolSize:线程池最大线程数量
  long keepAliveTime: 线程空闲后的存活时间    (控制超出corePoolSize的线程数存活的时间)
  TimeUnit unit:线程保持活动的时间单位
  BlockingQueue<Runnable> workQueue :用与存放任务的阻塞队列

  defaultHandler :当队列和最大线程池都满了之后的饱和策略

 

private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
//32位Integer  Integer.SIZE
//COUNT_BITS:表示线程数量   COUNT_BITS = 29   
private static final int COUNT_BITS = Integer.SIZE - 3;
private static final int CAPACITY   = (1 << COUNT_BITS) - 1;

// runState is stored in the high-order bits
//高三位表示状态码
private static final int RUNNING    = -1 << COUNT_BITS; //111
private static final int SHUTDOWN   =  0 << COUNT_BITS; //000
private static final int STOP       =  1 << COUNT_BITS; //001
private static final int TIDYING    =  2 << COUNT_BITS; //010
private static final int TERMINATED =  3 << COUNT_BITS; //011

线程池状态

RUNNING

    (1)说明:运行状态,线程池创建后就处于Running

      (2)  切换:

SHUTDOWN

      (1)说明 :关闭,不接受新任务,但是队列中的任务还是会完成

        (2)切换:pool执行shutdown()方法时,转换成这个状态

STOP

       (1)说明:停止,不接受新任务,也不执行队列中的任务。

       (2)切换: pool执行shutdownNow(),转换成这个状态

TIDYING

       (1)说明: 整理,

       (2)

TERMINATED

线程池

 

 

 public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
        return new ScheduledThreadPoolExecutor(corePoolSize);
    }

 

MaxpoolSize   线程池最大线程数

CorePoolSize  核心线程池要保留在池中的线程数,即使它们处于空闲状态。生命周期与线程池生命周期相同

 

创建线程的方式

   New Thread   (代表真正意义的线程) 生命周期

  Implement Runnable  对run方法进行了重写(运行的过程还是需要借助Thread, 需要把对象传递到Thread中取)

 

execute方法如何执行的

 public void execute(Runnable command) {
        if (command == null)
            throw new NullPointerException();
        //根据位运算拿到当前线程池的状态码(高三位)
        int c = ctl.get();
        if (workerCountOf(c) < corePoolSize) { //当前工作线程数量< 核心线程数量
            if (addWorker(command, true)) //用一个线程处理
                return;
            c = ctl.get();
        }
        if (isRunning(c) && workQueue.offer(command)) { //运行状态时,写入阻塞队列
            int recheck = ctl.get(); //再次获取状态码
            if (! isRunning(recheck) && remove(command))//双重检测,再次验证状态码
                reject(command);//根据策略 拒绝
            else if (workerCountOf(recheck) == 0)
                addWorker(null, false);//执行任务
        }
        else if (!addWorker(command, false))//尝试新建一个线程 如果失败
            reject(command);//拒绝
    }

如何配置线程池

线程池肯定不是越大越好

通常时根据这批任务执行的性质来确定的

IO密集型任务:由于线程并不是一直在运行,所以可以尽可能的多配置线程,比如CPU个数*2

CPU密集型任务(大量复杂的运算)应当分配较少的线程,比如CPU个数相当的大小

线程池关闭

有运行任务自然有关闭任务 使用shutdown()/shutdownNow()

但是两者有区别

shutdown() 执行后停止接受新任务,会把队列中任务执行完

shutdownNow() 停止接受新任务,但会中断所有任务,将线程池状态变为stop