线程池(详细版)

目录:

1、使用callable创建线程

2、使用ExectorService、callable、Future实现有返回结果的线程

3、两种基础的线程池

        1)ThreadPollExecutor

        2)ScheduledThreadPoolExecutor

4、线程池的好处

5、线程池的管理过程:

6、类层次结构

7、ThreadPoolExecutor带七个参数的构造方法

8、RejectedExecutionHandler handler拒绝任务处理器

9、 使用 Executors 创建几种常见的线程池

线程池:

        线程是一个操作系统概念,操作系统负责这个线程的创建、挂起、运行、阻塞和终结操作,每个用户都来访问,可能就是1s的事,但是服务器要为你单独创建线程,那么物理机基本上都创建线程而业务请求处理资源少了

1、使用callable创建线程

public class Test1 {
			public static void main(String[] args) throws InterruptedException, ExecutionException {
				MyThread th=new MyThread();
				
				FutureTask<String> future=new FutureTask<>(th);
				
				Thread t=new Thread(future);
				t.start();
				
				String sum=future.get();  //这个方法会阻塞,一直等到线程执行完
				System.out.println(sum);
				
				System.out.println("-------main结束 ------");	
			}
		}
class MyThread implements Callable<String>{
			public String call() throws Exception {	
				int sum=0;
				for(int i=0;i<=100;i++) {
					sum+=i;
					System.out.println("进行到第" +i +"次计算");
				}
				
				return "计算结果:"+sum; 
			}
		}

call()方法:1)call()方法有返回值

                        2)可以抛出异常

                        3)可以声明泛型

class MyThread implements callable<String>{}
怎么实现? (1)MyThread th=new MyThread();
          (2)FutureTask<String> future=new FuturnTask<>(th);//返回实现类的返回值future.get()
          (3)Thread t=new Thread(future);
          (4)t.start();
    

2、使用ExectorService、callable、Future实现有返回结果的线程

        1)它们三个都属于Executor框架

        如果想得到线程的返回值,则必须实现callable接口,可获取一个Future对象,然后用

        Future.get()方法获取值

        #get方法会阻塞(如果没有获取到返回值的话)

        2)创建线程池

        ExecutorService threadPool=Executors.newFixedThreadPool(5);//5代表线程池的核心线程数

        threadPool.submit(new MyThread());//返回值为 Future

       newFixedThreadPool的核心线程数和最大线程数相同

//例子 提交一个线程到线程池中去执行


			public class Test2 {
				public static void main(String[] args) throws InterruptedException, ExecutionException {
					ExecutorService threadPool= Executors.newFixedThreadPool(5);	
					Future<Object> future= threadPool.submit(new MyThread());
					System.out.println(future.get());
				}
			}
			
			
			class MyThread implements Callable<Object>{
				public Object call() throws Exception {
					String name=Thread.currentThread().getName();
					
					System.out.println(name+"开始");
					int time=new Random().nextInt(3000);
					Thread.sleep(time);
					System.out.println(name+"结束");
					
					return name+"执行了"+time +" ms"; 
				}	
			}

//例子 提交10个线程到线程池中执行     

	
			public class Test2 {
				public static void main(String[] args) throws InterruptedException, ExecutionException {
					ExecutorService threadPool= Executors.newFixedThreadPool(5);	 //线程池核心线程数是5
					List<Future<Object>> futureList=new ArrayList<>();
					
					for(int i=0;i<10;i++) {
						Future<Object> future= threadPool.submit(new MyThread());
						futureList.add(future); 
					}
					
					threadPool.shutdown();  //说明不在再线程池中提交了	
					
					for(Future<Object> f:futureList) {
						System.out.println(f.get());
					}
				}
			}
			
			
			class MyThread implements Callable<Object>{
				public Object call() throws Exception {
					String name=Thread.currentThread().getName();
					
					System.out.println(name+"开始");
					int time=new Random().nextInt(3000);
					Thread.sleep(time);
					System.out.println(name+"结束");
					
					return name+"执行了"+time +" ms"; 
				}	
			}
			

//例子 把普通的实现Runnable 接口的线程交给线程池去处理


     public class Test3 {
				public static void main(String[] args) {
				   ExecutorService pool=Executors.newCachedThreadPool();
				   
				   for(int i=0;i<5;i++) {
					   pool.submit(new SaleThread());
				   }
				   
				    pool.shutdown();
				}
			}
					
			class SaleThread implements Runnable{
				public void run() {
					while(true) {
						System.out.println("线程"+Thread.currentThread().getName()+" 正在运行 ");
					}
				}
			}
			

3、两种基础的线程池

        1)ThreadPollExecutor

        2)ScheduledThreadPoolExecutor

上面两种方式都实现了ExecutorService接口

4、线程池的好处

        1)降低资源的消耗,通过重复利用它创建的线程降低线程创建和销毁造成的消耗

        2)提高响应速度。当任务到达时,任务可以不需要等到线程创建就能立即执行

        3)提高线程的可管理性,线程是稀缺资源,如果无限制的创建,不仅会消耗系统资源,还会降低系统的稳定性,使用线程池可以进行统一的分配、调优和监控

5、线程池的管理过程:

corepoolsize:核心线程数

ExecutorService threadPool=Executors.newFixedThreadPool(5);

        首先创建一个线程,提交任务到线程池中,直至任务数量增大到核心线程数,再有下一个线程的时候,当他判断的时候发现核心线程数已经满了,然后他就往任务队列里面放置,下面再来的任务都因为核心线程数满了,就放置在任务队列中,直至任务队列也饱满,此时还在来任务,他发现核心线程数和任务队列都是满的,然后他去判断有没有达到最大线程数,发现没有,就去执行任务,此时,后面再来的任务发现核心线程在忙,任务队列也满了,执行的任务也达到了最大线程数,此时再来的线程就会执行拒绝策略,需要handler来处理了,或者丢弃新任务,或者拒绝新任务,或者挤占掉已有的任务。在任务队列和线程池都饱和的情况下,一旦有线程处于等待(任务处理完毕,没有新任务)状态的时间超过keepAliveTime,则该线程终止,也就是说池中的线程数量会逐渐降低

6、类层次结构

        (1)Executor     接口

        (2)ExecutorService   接口,继承自Executor

        (3)ThreadPoolExecutor  继承自ExecutorService,实际创建线程池就是创建这个类的对象

        (4)ScheduledThreadPoolExecutor 继承自ThreadPoolExecutor,可以创建带有任务调度的线程池

        (5)Executors  是个工具类,可以帮我们创建各种不同类型的线程池

1)Executor     接口

public interface Executor{
    void execute(Runnable command);
}

2)ExecutorService

public interface ExecutorService extends Executor {
			  			void shutdown();  //shutdown,执行后不再接收新任务,如果里面有任务,就执行完
					    List<Runnable> shutdownNow(); //立即停
					    boolean isShutdown();
					    boolean isTerminated();
					    boolean awaitTermination(long timeout, TimeUnit unit) //等多久后发出中断
					        throws InterruptedException;
					
					    <T> Future<T> submit(Callable<T> task);	
					    <T> Future<T> submit(Runnable task, T result);
					    Future<?> submit(Runnable task);
					    <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks)
					    <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit)
					    <T> T invokeAny(Collection<? extends Callable<T>> tasks)
					    <T> T invokeAny(Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit) ..;
	 				}
								

 3) ThreadPoolExecutor   我们可以认为它的类对象就是线程池
             这是它带七个参数的构造方法

 public ThreadPoolExecutor(
		    				int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory,
                              RejectedExecutionHandler handler) {
						        if (corePoolSize < 0 ||
						            maximumPoolSize <= 0 ||
						            maximumPoolSize < corePoolSize ||
						            keepAliveTime < 0)
						            throw new IllegalArgumentException();
						        if (workQueue == null || threadFactory == null || handler == null)
						            throw new NullPointerException();
						        this.acc = System.getSecurityManager() == null ?
						                null : AccessController.getContext();
						               
						        this.corePoolSize = corePoolSize;
						        this.maximumPoolSize = maximumPoolSize;
						        this.workQueue = workQueue;
						        this.keepAliveTime = unit.toNanos(keepAliveTime);
						        this.threadFactory = threadFactory;
						        this.handler = handler;
						    }
						    

7、ThreadPoolExecutor带七个参数的构造方法***********************重要***************

public ThreadPoolExecutor(
    int corePoolSize,//核心线程数
    int maximumPoolSize,//最大线程数
    long keepAliveTime,//线程的最大生命周期
    TimeUnit unit,//时间计量单位
    BlockingQueue<Runnable> workQueue,//任务队列
    ThreadFactory threadFactory,//定义如何启动一个线程,可以设置线程的名称,并且可以确定是否是后台线程
    RejectedExecutionHandler handler){//拒绝策略
    .......
}

详细解说线程池的七个参数:

1)corePoolSize 核心线程数

核心线程:线程池新建线程的时候,如果当前线程总数小于corePoolSize,则新建的是核心线程,如果超过corepoolsize,则新建的是非核心线程,核心线程默认情况下会一直存活在线程池中,即使这个线程啥也不干(闲置状态),如果指定 ThreadPoolExecutor的allowCoreThreadTimeOut这个属性为true,那么核心线程处于闲置状态超过一定事件(时长由下面参数决定),就会被销毁掉

2)maximumPoolSize    线程池中的最大线程数

最大线程数=核心线程数+非核心线程数

3)keepAliveTime 线程的最大生命周期

        一个非核心线程,如果处于闲置状态的时长超过这个参数锁设定的时长,就会被销毁掉,

        如果将参数allowcorethreadtimeout=true,则这个线程的最大生命周期设置的超时时间也会作用于核心线程

4)TimeUnit unit 时间计量单位,是一个枚举类型(enumerate)

                            NANOSECONDS : 1微毫秒 = 1微秒 / 1000
                            MICROSECONDS : 1微秒 = 1毫秒 / 1000
                            MILLISECONDS : 1毫秒 = 1秒 /1000
                            SECONDS : 秒

                            MINUTES : 分
                            HOURS : 小时
                            DAYS : 天

                传的时候,可以:TimeUnit.SECONDS 这样写

5)BlockingQueue<Runnable> workQueue

        线程池中的任务队列,维护者等待执行的Runnable对象任务队列。当线程池中的线程都处于运行状态,而此时任务数量继续增加,则需要一个容器来容纳这些任务,这就是任务队列。这个任务队列是一个阻塞式的单端队列

常用的workqueue类型

###synchronousQueue(同步队列)

这个队列接收到新的任务的时候,会直接提交给线程处理,而不保留它,如果所有的线程都在工作怎么办?那就新建一个线程来处理这个这个任务!所以为了保证不出现 <线程数达到maximumPoolSize而不能新建线程>的错误, 使用这个类型队列的时候,maximumPoolSize一般指定成Integer.MAX_VALUE,即无限大

###LinkedBlockingQueue队列

这个队列接收到任务的时候,如果当前线程小于核心线程数,则新建线程处理任务,,如果当前线程数等于核心线程数,则进入等待队列等待。由于这个队列没有最大值限制,即所有超过核心线程数的任务都会被加入到队列中,这也就导致了maximumPoolSize的设定失效,因为总线程数永远不会超过核心线程数

###ArrayBlockingQueue

这个队列接到新任务时,如果当前任务没有达到核心线程的数量,则新建核心线程执行任务,如果达到了核心线程,则入队等候,如果队列已满,则新建非核心线程执行任务,如果总线程数量达到了最大线程数,并且队列也已满,则执行拒绝策略

###DelayQueue

队列内的元素必须实现Delay接口,这就意味着你传进去的任务必须先实现Delay接口,这个队列接到新的任务时,首先先入队,只有达到了指定的延时时间,才会去执行任务

8、RejectedExecutionHandler handler拒绝任务处理器

由于超过线程容量和队列容量而对继续增加的任务进行处理的程序

jdk内置了四种拒绝策略

1)AbortPolicy  中止策略

为java的默认策略,不执行此任务,而是直接抛出一个运行时异常

 切记ThreadPoolExecutor.execute需要try catch,否则程序会直接退出。

 2)CallerRunsPolicy
        在任务被拒绝添加后,会在调用execute()方法 ( 或submit() )的线程来执行被拒绝的任务。  除非executor被关闭,否则任务不会被丢弃。显然这样不会真的丢弃任务,但是, 调用者线 程性能可能急剧下降。
        在调用execute的线程里面执行此command,会阻塞入口
                                 
3)DiscardOldestPolicy策略
           从队列里面抛弃head的一个任务,并再次execute 此task
                            
4)DiscardPolicy策略
          直接抛弃,任务不执行,空方法。

9、 使用 Executors 创建几种常见的线程池
    它是一个工具类, 可以帮我们创建不同类型的线程池
          1.可缓存线程池 CachedThreadPool()
           调用 ExecutorService pool= Executors.CachedThreadPool();
           源码:

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

     
   根据源码可以看出:
         (1).这种线程池内部没有核心线程,线程的数量是有没限制的。
         (2).在创建任务时,若有空闲的线程时则复用空闲的线程,若没有则新建线程。
         (3).没有工作的线程(闲置状态)在超过了60S还不做事,就会销毁。

                 
        2.FixedThreadPool 定长线程池
          调用:ExecutorService pool= Executors.FixedThreadPool();
          源码:          

public static ExecutorService newFixedThreadPool(int nThreads) {
         return new ThreadPoolExecutor(nThreads, nThreads,  //最大线程数等于核心线程数
          0L, TimeUnit.MILLISECONDS,
          new LinkedBlockingQueue<Runnable>());
                            }

 根据源码可以看出:
             该线程池的最大线程数等于核心线程数,所以在默认情况下,该线程池的线程不会因为闲  置状态超时而被销毁。
            这个实例会复用 固定数量的线程 处理一个 共享的无边界队列 。任何时间点,最多有 nThreads 个线程会处于活动状态执行任务。
            如果当所有线程都是活动时, 有多的任务被提交过来,  那么它会一致在队列中等待直到有线程可用。如果任何线程在执行过程中因为错误而中止,新的线程会替代它的位置来执行后续的任务。所有线程都会一致存于线程池中,直到显式的执行 ExecutorService.shutdown() 关闭。
                            

                            
      3.SingleThreadPool (可以让线程顺次执行)
        调用 ExecutorService pool = Executors.newSingleThreadExecutor();
       源码:
                                

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


     、根据源码可以看出:
                 核心线程数是 1 , 最大线程数 1
                   (1).有且仅有一个工作线程执行任务
                   (2).所有任务按照指定顺序执行,即遵循队列的入队出队规则

                                    
                    
      4.ScheduledThreadPool  可以进行延迟执行和周期性重复执行 
              调用 ExecutorService pool = Executors.newScheduledThreadPool(int corePoolSize));
              
               源码:
             

 public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize{
         return new ScheduledThreadPoolExecutor(corePoolSize);
 }
                            
 public ScheduledThreadPoolExecutor(int corePoolSize) {
         super(corePoolSize, Integer.MAX_VALUE,
                DEFAULT_KEEPALIVE_MILLIS, MILLISECONDS,
                new DelayedWorkQueue());
 }


                            
               DEFAULT_KEEPALIVE_MILLIS就是默认10L,这里就是10秒。这个线程池有点像是 CachedThreadPool和FixedThreadPool 结合了一下。
            1.不仅设置了核心线程数,最大线程数也是 Integer.MAX_VALUE。
             2.这个线程池是上述4个中为唯一个有延迟执行和周期执行任务的线程池。

                            

	//表示在3秒之后开始执行我们的任务。
mScheduledThreadPool.schedule(new Runnable() {
							           
			public void run() {
					 //....
			}
}, 3, TimeUnit.SECONDS);
							        
//延迟3秒后执行任务,从开始执行任务这个时候开始计时,每7秒执行一次不管执行任务需要多长的时间。 
mScheduledThreadPool.scheduleAtFixedRate(new Runnable() {
			
			 public void run() {
				 //....
			 }
 },3, 7, TimeUnit.SECONDS);
							        
 /**延迟3秒后执行任务,从任务完成时这个时候开始计时,7秒后再执行,
	*再等完成后计时7秒再执行也就是说这里的循环执行任务的时间点是
	*从上一个任务完成的时候。
	*/
mScheduledThreadPool.scheduleWithFixedDelay(new Runnable() {
	
	public void run() {
		  //....
	 }
},3, 7, TimeUnit.SECONDS);
							        
							        
//例子	  scheduleWithFixedDelay       
public class Test5 {
		public static void main(String[] args) {
			ScheduledExecutorService pool=Executors.newScheduledThreadPool(5);
								
								    for(int i=0;i<10;i++) {
									//pool.submit(new MyWorker()); 
									
									
									//参数说明:
									//3 表示3秒以后开始运行
									//7 表示线程执行完任务以后,再过7秒,再循环进行
									//TimeUnit.SECONDS 表示以秒为单位
									pool.scheduleWithFixedDelay(new MyWorker(), 3, 7, TimeUnit.SECONDS);
								}
							}
						
						}
						
						
class MyWorker implements Runnable{
			public void run() {
								
				int time=new Random().nextInt(3000);
				try {
						Thread.sleep(time);
				} catch (InterruptedException e) {
						e.printStackTrace();
				 }
								
								System.out.println(Thread.currentThread().getName()+"工作了 "+time +" ms");	
							}
						}
						

用它指行一般任务和前面的线程池用法大致相同,我们主要关心它用于延时执行任务和周期执行任务的用法

线程池创建示例

	public class Test6 {
		
			public static void main(String[] args) throws InterruptedException, ExecutionException {
				//核心线程数
				int corePoolSize=5;
				
				//最大线程数
				int maxinumPoolSize=7;
				
				//超时时间
				int keepAliveTime=60;
				
				//时间单位
				TimeUnit timeUnit=TimeUnit.SECONDS;
				
				//线和池的缓存队列
				BlockingQueue<Runnable> workQueue=new ArrayBlockingQueue<>(10);
				
				//工厂
				ThreadFactory factory=Executors.defaultThreadFactory();
				
				//拒绝策略
				RejectedExecutionHandler myHandler=new RejectedExecutionHandler() {
					public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
						System.out.println(r+" 被丢弃了");
					}
				};
				
				ExecutorService	 pool=new ThreadPoolExecutor(corePoolSize,maxinumPoolSize,keepAliveTime,timeUnit,workQueue,factory,myHandler);
		
				List<Future <Integer> > futureList=new ArrayList<>();
				for(int i=0;i<20;i++) {
					Future <Integer> future=pool.submit(new Sporter());
					futureList.add(future);
				}
				
				pool.shutdown();
				
				for(Future <Integer> f: futureList) {
					System.out.println(f.get());
				}
			}
		}
		
		
		class Sporter implements Callable<Integer>{
			public Integer call() throws Exception {
				String name=Thread.currentThread().getName(); 
				System.out.println(name +"开始");
				int time=new Random().nextInt(3000);
				System.out.println(name +"结束,执行了" +time +" ms");	
				
				return time;
			}	
		}

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
MFC(Microsoft Foundation Class)是一套用于开发Windows桌面应用程序的C++类库。MFC提供了许多功能和工具,包括线程池,用于处理并发执行的任务。下面是一个使用MFC线程池详细示例: 1. 首先,在你的MFC应用程序中,包含头文件 afxmt.h,该文件定义了MFC中线程池相关的类和函数。 2. 创建一个继承自CWinApp的应用程序类,并在构造函数中调用SetAppCompatiblityMode函数,以确保应用程序与线程池兼容。示例代码如下: ```cpp class CMyApp : public CWinApp { public: CMyApp() { SetAppCompatiblityMode(_WIN32_WINNT_WIN7); } // ... }; ``` 3. 在你的主窗口类中,添加一个成员变量用于表示线程池,并在窗口的创建过程中初始化该线程池。示例代码如下: ```cpp class CMyWnd : public CFrameWnd { private: CThreadPool m_threadPool; public: CMyWnd() { m_threadPool.Initialize(); } // ... }; ``` 4. 在需要使用线程池的地方,创建一个继承自CWorkerThread的工作线程类,并重写其Run方法,该方法定义了线程需要执行的任务。示例代码如下: ```cpp class CMyWorkerThread : public CWorkerThread { public: virtual DWORD Run() { // 执行任务的代码 // ... return 0; } }; ``` 5. 在主窗口类中,创建并启动工作线程。示例代码如下: ```cpp void CMyWnd::StartWorkerThread() { CMyWorkerThread* pWorkerThread = new CMyWorkerThread(); m_threadPool.AddWorkerThread(pWorkerThread); pWorkerThread->Run(); } ``` 通过以上步骤,你就可以在MFC应用程序中使用线程池来处理并发任务了。线程池会自动管理线程的创建、销毁和调度,你只需要定义好工作线程类的任务代码即可。 注意:以上示例代码仅为演示用途,实际使用中需要根据具体需求进行适当的修改和扩展。另外,MFC线程池的使用还涉及更多细节,如任务队列、线程同步等,你可以参考MFC文档和相关教程来深入学习和理解。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值