17.多线程(4)

13.线程池

1.什么是线程池

  • 一个创建线程的容器,可以通过该容器去创建不同业务场景的线程来完成不同的需求

2.为什么需要线程池

  • 线程缺少统一的管理,占用过多的系统资源

  • 缺乏更多功能.比如定时执行、定期执行等

  • 使用线程池的优势

    • 可以重用存在的线程,减少对象的创建、消亡的开销
    • 有效的控制并发数,提高系统资源的使用率
    • 线程池可以实现定时执行、定期执行等功能.

3.如何创建线程池

  • 线程池所在的包都在java.util.concurrent包下,顶级的接口Executors,真正的线程池接口是ExecutorService
方法名说明
newCachedThreadPool()创建了一个可缓存的线程池,有任务才会创建新的线程
newSingleThreadPool()创建一个单线程,线程池中只有一个线程
newEixedThreadPool(int numThreads)创建一个固定长度的线程池,空闲线程会一直保留,numThreads设定线程的数量
newScheduledThreadPool(int corePoolSize)创建一个固定长度的线程池,而且一延迟或定时的方式来执行线程

案例1:newCachedThreadPool()创建了一个可缓存的线程池

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class Test {
	public static void main(String[] args) {
		
		//创建了一个有缓存线程池,有任务时才会创建更多的线程
		ExecutorService cachedThread = Executors.newCachedThreadPool();
		for (int i = 1; i <= 10; i++) {
        	try {
				Thread.sleep(500);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			cachedThread.execute(new MyRunable(i));
		}

	}
}

结果

pool-1-thread-1执行了1
pool-1-thread-1执行了2
pool-1-thread-1执行了3
pool-1-thread-1执行了4
pool-1-thread-1执行了5
pool-1-thread-1执行了6
pool-1-thread-1执行了7
pool-1-thread-1执行了8
pool-1-thread-1执行了9
pool-1-thread-1执行了10
10个任务被同一个线程执行了,因此减少了线程的创建和销毁,大大提高了效率

ExecutorService cachedThread = Executors.newCachedThreadPool();创建了一个有缓存线程池,当去执行1-10这个任务的时候,忙的过来的时候只会创建一个线程,但是该线程忙不过来的时候,会根据任务创建出更多的线程来完成任务

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class Test {
	public static void main(String[] args) {
		
		//创建了一个有缓存线程池,有任务时才会创建更多的线程
		ExecutorService cachedThread = Executors.newCachedThreadPool();
		for (int i = 1; i <= 10; i++) {
//			try {
//				Thread.sleep(500);
//			} catch (InterruptedException e) {
//				e.printStackTrace();
//			}
			cachedThread.execute(new MyRunable(i));
		}

	}
}

结果

pool-1-thread-1执行了1
pool-1-thread-2执行了2
pool-1-thread-3执行了3
pool-1-thread-5执行了5
pool-1-thread-6执行了6
pool-1-thread-2执行了10
pool-1-thread-7执行了7
pool-1-thread-4执行了4
pool-1-thread-8执行了8
pool-1-thread-9执行了9
因为线程休眠被取消了,执行的时间变短了,线程池让更多的线程参与到任务里面来,因此线程参与到任务里面来了,因此创建了9个线程参与进来,有的线程在执行完任务后,会去接新的任务,比如2号线程执行了两次
  • 当多个线程在执行任务时,如果有线程阻塞,该线程池会创建出更多的线程来完成任务,提高性能。

案例2:newSingleThreadExecutor()创建单线程池

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class Test {
	public static void main(String[] args) {
		
		//创建了一个只有一个线程的线程池,叫单线程池
		ExecutorService cachedThread = Executors.newSingleThreadExecutor();
		for (int i = 1; i <= 10; i++) {
			cachedThread.execute(new MyRunable(i));
		}

	}
}

结果

pool-1-thread-1执行了1
pool-1-thread-1执行了2
pool-1-thread-1执行了3
pool-1-thread-1执行了4
pool-1-thread-1执行了5
pool-1-thread-1执行了6
pool-1-thread-1执行了7
pool-1-thread-1执行了8
pool-1-thread-1执行了9
pool-1-thread-1执行了10

案例3:newFixedThreadPool(3)创建了固定线程个数的线程池

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class Test {
	public static void main(String[] args) {
		
		//创建了固定线程个数的线程池
		ExecutorService cachedThread = Executors.newFixedThreadPool(3);
		for (int i = 1; i <= 10; i++) {
			cachedThread.execute(new MyRunable(i));
		}

	}
}

结果

pool-1-thread-1执行了1
pool-1-thread-3执行了3
pool-1-thread-2执行了2
pool-1-thread-3执行了5
pool-1-thread-1执行了4
pool-1-thread-3执行了7
pool-1-thread-2执行了6
pool-1-thread-3执行了9
pool-1-thread-1执行了8
pool-1-thread-2执行了10

案例4:scheduleAtFixedRate(new MyRunable(), 5, 2, TimeUnit.SECONDS)创建定时线程池

import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

public class Test {
	public static void main(String[] args) {
		ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(3);
		/**
		 * 	参数1:需要执行线程任务的对象
		 * 	参数2:延迟的单位
		 * 	参数3:间隔时间单位(每隔这个事件单位执行一次)
		 * 	参数4:时间单位,具体参见TimeUnit枚举,我在这设置了秒
		 */
		scheduledThreadPool.scheduleAtFixedRate(new MyRunable(), 5, 2, TimeUnit.SECONDS);
	}
}

结果

pool-1-thread-1延迟5秒执行每两秒执行一下该任务
pool-1-thread-1延迟5秒执行每两秒执行一下该任务
pool-1-thread-2延迟5秒执行每两秒执行一下该任务
pool-1-thread-2延迟5秒执行每两秒执行一下该任务
pool-1-thread-3延迟5秒执行每两秒执行一下该任务
pool-1-thread-1延迟5秒执行每两秒执行一下该任务
pool-1-thread-2延迟5秒执行每两秒执行一下该任务
pool-1-thread-3延迟5秒执行每两秒执行一下该任务
pool-1-thread-3延迟5秒执行每两秒执行一下该任务
pool-1-thread-3延迟5秒执行每两秒执行一下该任务

案例5:创建自定义线程池

  • 如果上面定义好的线程池类型不符合我们的业务要求,那么我们可以自定义线程池

  • 情况1

    import java.util.concurrent.ArrayBlockingQueue;
    import java.util.concurrent.ThreadPoolExecutor;
    import java.util.concurrent.TimeUnit;
    
    public class Test {
    	public static void main(String[] args) {
    		//创建一个自定义的线程池
    		/**
    		 * 	参数1:开启线程的固定数量为5个
    		 * 	参数2:当任务数量多的时候,最大的线程数量最大为7(有两个临时工)
    		 * 	参数3:临时线程等待接收任务的时间,如果超出300秒没有接收到新任务会自动销毁
    		 * 	参数4:时间单位,设置了秒
    		 * 	参数5:等待执行的任务序列为4个(其中有4个任务需要等待线程池中7个线程执行完任务再去执行)
    		 */
    		ThreadPoolExecutor executor = new 
    				ThreadPoolExecutor(5, 7, 300, TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(4));
    		
    		for (int i = 1; i <= 12; i++) {
    			executor.execute(new MyRunable(i));
    			System.out.println("线程池中的线程数:"+executor.getPoolSize()+
    					",队列中等待执行任务数:"+executor.getQueue().size()
    					+",已经执行完的任务数:"+executor.getMaximumPoolSize());
    		}
    		executor.isShutdown();
    	}
    }
    
  • 结果

    正在执行任务1
    线程池中的线程数:1,队列中等待执行任务数:0,已经执行完的任务数:7
    线程池中的线程数:2,队列中等待执行任务数:0,已经执行完的任务数:7
    正在执行任务2
    线程池中的线程数:3,队列中等待执行任务数:0,已经执行完的任务数:7
    线程池中的线程数:4,队列中等待执行任务数:0,已经执行完的任务数:7
    正在执行任务3
    线程池中的线程数:5,队列中等待执行任务数:0,已经执行完的任务数:7
    线程池中的线程数:5,队列中等待执行任务数:1,已经执行完的任务数:7
    线程池中的线程数:5,队列中等待执行任务数:2,已经执行完的任务数:7
    线程池中的线程数:5,队列中等待执行任务数:3,已经执行完的任务数:7
    线程池中的线程数:5,队列中等待执行任务数:4,已经执行完的任务数:7
    线程池中的线程数:6,队列中等待执行任务数:4,已经执行完的任务数:7
    正在执行任务4
    线程池中的线程数:7,队列中等待执行任务数:4,已经执行完的任务数:7
    正在执行任务5
    正在执行任务10
    Exception in thread "main" 正在执行任务11
    java.util.concurrent.RejectedExecutionException: Task day08.MyRunable@42a57993 rejected from java.util.concurrent.ThreadPoolExecutor@75b84c92[Running, pool size = 7, active threads = 7, queued tasks = 4, completed tasks = 0]
    	at java.util.concurrent.ThreadPoolExecutor$AbortPolicy.rejectedExecution(Unknown Source)
    	at java.util.concurrent.ThreadPoolExecutor.reject(Unknown Source)
    	at java.util.concurrent.ThreadPoolExecutor.execute(Unknown Source)
    	at day08.Test.main(Test.java:21)
    任务11执行完毕!
    任务3执行完毕!
    任务2执行完毕!
    正在执行任务6
    任务1执行完毕!
    正在执行任务9
    任务10执行完毕!
    任务4执行完毕!
    任务5执行完毕!
    正在执行任务7
    正在执行任务8
    任务9执行完毕!
    任务8执行完毕!
    任务7执行完毕!
    任务6执行完毕!
    
  • 报错原因

    • 因为12个任务,你只安排了7个线程去执行并且4个信息队列存储任务,第12个任务存储不下被销毁,容量不够,报错

    • 解决办法:很简单,增加最大线程容量或者扩大队列

    • ThreadPoolExecutor executor = new 
      				ThreadPoolExecutor(5, 8, 300, TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(4));
      		
      
    • 结果

      正在执行任务1
      线程池中的线程数:1,队列中等待执行任务数:0,已经执行完的任务数:8
      线程池中的线程数:2,队列中等待执行任务数:0,已经执行完的任务数:8
      线程池中的线程数:3,队列中等待执行任务数:0,已经执行完的任务数:8
      正在执行任务2
      线程池中的线程数:4,队列中等待执行任务数:0,已经执行完的任务数:8
      正在执行任务4
      线程池中的线程数:5,队列中等待执行任务数:0,已经执行完的任务数:8
      正在执行任务3
      线程池中的线程数:5,队列中等待执行任务数:1,已经执行完的任务数:8
      线程池中的线程数:5,队列中等待执行任务数:2,已经执行完的任务数:8
      线程池中的线程数:5,队列中等待执行任务数:3,已经执行完的任务数:8
      线程池中的线程数:5,队列中等待执行任务数:4,已经执行完的任务数:8
      正在执行任务5
      线程池中的线程数:6,队列中等待执行任务数:4,已经执行完的任务数:8
      线程池中的线程数:7,队列中等待执行任务数:4,已经执行完的任务数:8
      正在执行任务10
      线程池中的线程数:8,队列中等待执行任务数:4,已经执行完的任务数:8
      正在执行任务11
      正在执行任务12
      任务11执行完毕!
      任务12执行完毕!
      正在执行任务6
      正在执行任务7
      任务3执行完毕!
      任务5执行完毕!
      任务4执行完毕!
      任务2执行完毕!
      正在执行任务9
      任务1执行完毕!
      任务10执行完毕!
      正在执行任务8
      任务8执行完毕!
      任务9执行完毕!
      任务7执行完毕!
      任务6执行完毕!
      
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值