线程池

为什么使用线程池

不用线程池

public class PoolTest {
	public static void main(String[] args) {
		Long start=System.currentTimeMillis();
		List list=new ArrayList();
		Random r=new Random();
		for(int i=0;i<100000;i++)
		{
			Thread t=new Thread(new Runnable() {
				@Override
				public void run() {
					list.add(r.nextInt());
				}
			});
			t.start();
			try {
				t.join();
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		Long end=System.currentTimeMillis();
		System.out.println(end-start);
		System.out.println(list.size());
	}
}
10985
100000

用了线程池

public class PoolTest {
	public static void main(String[] args) {
		Long start=System.currentTimeMillis();
		List list=new ArrayList();
		Random r=new Random();
		ExecutorService es = Executors.newSingleThreadExecutor();
		for(int i=0;i<100000;i++)
		{
			es.execute(new Runnable() {
				@Override
				public void run() {
					list.add(r.nextInt());
				}
			});
		}
		es.shutdown();
		try {
			es.awaitTermination(2, TimeUnit.DAYS);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		Long end=System.currentTimeMillis();
		System.out.println(end-start);
		System.out.println(list.size());
	}
}

30
100000

明显性能大大大大大大提高了,为什么呢?
因为Thread t=new Thread(new Runnable(){})方法太耗时了.多线程的基本思想是执行任务,任务被封装在Runable的run()方法里面。那么可以通过复用的思想,让一个Thread对象多次传入Runnable参数。

线程池的组成部分

1.核心线程,用于执行任务的线程
2.阻塞队列,当核心线程都在工作,新加入的任务就是放入阻塞队列中
2.救急线程,当阻塞队列也满了,使用救急线程执行任务。救急线程有等待时间。如果超过了等待时间 还是没有任务,就杀死救急线程
4.线程工厂,没什么用,负责给线程池的线程编号
5.拒绝策略,当救急线程也不管用了,使用拒绝策略处理新的任务

线程池的创建方法

1.自定义方法

public class MyThreadPoolDemo {
    public static void main(String[] args) {
        ExecutorService threadPool = new ThreadPoolExecutor(
                2,        //核心线程数
                5,        //总线程数量,救急线程数量=5-2
                1L,       
                TimeUnit.SECONDS,    //这两个是救急线程的等待时间
                new LinkedBlockingQueue<>(3),     //阻塞队列,长度是3,如果不指定,就是最大整数
                Executors.defaultThreadFactory(),  //工厂方法
                new ThreadPoolExecutor.DiscardOldestPolicy());  //拒绝策略
        try {
            for(int i = 0; i < 10; i++){
                int temp = i;
                threadPool.execute(()->{
                    System.out.println(Thread.currentThread().getName() + "\t 执行任务:" + temp);
                });
            }
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            threadPool.shutdown();
        }
    }
}

面试阿里的时候碰到了一个让我崩溃是问题,如果不指定阻塞队列长度会怎么样?答案非常简单。这个时候拒绝策略就失效了,如果任务量太大,会导致内存爆满。正式考虑到内存的承受能力,才需要设置阻塞队列长度和线程数量,并加入拒绝策略。
但是目前的自带的构造线程池方法,要么阻塞队列长度无限,要么救急线程无限,所以都是不推荐的方法。
2.newFixedThreadPool

public static ExecutorService newFixedThreadPool(int nThreads) {
 return new ThreadPoolExecutor(nThreads, nThreads,
 0L, TimeUnit.MILLISECONDS,
 new LinkedBlockingQueue<Runnable>());
}

特点
核心线程数 == 最大线程数(没有救急线程被创建),因此也无需超时时间。
阻塞队列是无界的,可以放任意数量的任务。但是容易造成系统爆满。
评价:适用于任务量已知,相对耗时的任务。
3. newCachedThreadPool

public static ExecutorService newCachedThreadPool() {
 return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
 60L, TimeUnit.SECONDS,
 new SynchronousQueue<Runnable>());
}

特点
核心线程数是 0, 最大线程数是 Integer.MAX_VALUE,救急线程的空闲生存时间是 60s,意味着全部都是救急线程(60s 后可以回收),没有数量限制。但是这容易造成内存崩塌。
队列采用了 SynchronousQueue 实现特点是,它没有容量,没有线程来取是放不进去的(一手交钱、一手交货),类似于缓存。
评价:整个线程池表现为线程数会根据任务量不断增长,没有上限,当任务执行完毕,空闲 1分钟后释放线程。 适合任务数比较密集,但每个任务执行时间较短的情况。
4.newSingleThreadExecutor

public static ExecutorService newSingleThreadExecutor() {
 return new FinalizableDelegatedExecutorService
 (new ThreadPoolExecutor(1, 1,
 0L, TimeUnit.MILLISECONDS,
 new LinkedBlockingQueue<Runnable>()));
}

使用场景:
希望多个任务排队执行。线程数固定为 1,任务数多于 1 时,会放入无界队列排队。任务执行完毕,这唯一的线程也不会被释放。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值