• Java并发编程从入门到精通


    1、什么是线程池(为什么使用线程池):
    2、Executor框架介绍:
      Java 5中引入的,其内部使用了线程池机制,在java.util.cocurrent 包下,通过该框架来控制线程的启动、执行和关闭(使用该框架来创建线程池),可以简化并发编程的操作;
      Executor框架包括:线程池,Executor,Executors,ExecutorService,CompletionService,Future,Callable等;
      ExecutorService接口继承自Executor接口,提供了更丰富的实现多线程的方法,一般用该接口来实现和管理多线程;
      ExecutorService的生命周期包括三种状态:运行、关闭、终止;创建后便进入运行状态;当调用了shutdown()方法时,便进入关闭状态,此时意味着ExecutorService不再接受新的任务,但它还在执行已经提交了的任务;当所有已经提交了的任务执行完后,便到达终止状态;如果不调用shutdown()方法,ExecutorService会一直处在运行状态,不断接收新的任务,执行新的任务,服务器端一般不需要关闭它,保持一直运行即可;
      更详细描述看:http://blog.csdn.net/ns_code/article/details/17465497

     1 /**
     2  * Executor执行Runnable任务
     3  */
     4 package thread05;
     5 
     6 import java.util.concurrent.ExecutorService;
     7 import java.util.concurrent.Executors;
     8 
     9 public class ExecutorsTest01
    10 {
    11     public static void main(String[] args)
    12     {
    13         // ExecutorService executorService = Executors.newCachedThreadPool();
    14         // ExecutorService executorService = Executors.newFixedThreadPool(4);
    15         ExecutorService executorService = Executors.newSingleThreadExecutor();
    16         
    17         // 创建5个任务并执行
    18         for(int i=0;i<5;i++)
    19         {
    20             executorService.execute(new RunnableTest2());
    21         }
    22         
    23         // 启动一次顺序关闭,执行以前提交的任务,但不接受新任务
    24         executorService.shutdown();
    25     }
    26 }
    27 
    28 class RunnableTest2 implements Runnable
    29 {
    30     // 具体的业务逻辑;一旦RunnableTest2实例对象传给ExecutorService的execute方法, 则该方法(任务)自动在一个线程上执行
    31     @Override
    32     public void run()
    33     {
    34         System.out.println(Thread.currentThread().getName() + "线程被调用了");
    35     }
    36     
    37 }
    Executor执行Runnable任务
     1 /**
     2  * Executor执行Callable任务
     3  */
     4 package thread05;
     5 
     6 import java.util.ArrayList;
     7 import java.util.List;
     8 import java.util.concurrent.Callable;
     9 import java.util.concurrent.ExecutionException;
    10 import java.util.concurrent.ExecutorService;
    11 import java.util.concurrent.Executors;
    12 import java.util.concurrent.Future;
    13 
    14 public class ExecutorsTest02
    15 {
    16     public static void main(String[] args)
    17     {
    18         ExecutorService executorService = Executors.newCachedThreadPool();
    19         List<Future<String>> futureList = new ArrayList<Future<String>>();
    20         
    21         // 创建10个任务并执行
    22         for(int i=0;i<10;i++)
    23         {
    24             // 使用ExecutorService执行Callable类型的任务,并将结果保存在future变量中
    25             Future<String> future = executorService.submit(new CallableTest2(i + ""));
    26             // 将任务执行结果存储到List中
    27             futureList.add(future);
    28         }
    29         
    30         // 遍历任务的结果 
    31         for(Future<String> f : futureList)
    32         {
    33             try
    34             {
    35                 // Future返回如果没有完成,则一直循环等待,直到Future返回完成
    36                 while(!f.isDone());
    37                 // 打印各个线程(任务)执行的结果 
    38                 System.out.println(f.get());
    39             }
    40             catch (InterruptedException | ExecutionException e)
    41             {
    42                 e.printStackTrace();
    43             }
    44             finally
    45             {
    46                 // 启动一次顺序关闭,执行以前提交的任务,但不接受新任务
    47                 executorService.shutdown();
    48             }
    49         }
    50         
    51     }
    52 }
    53 
    54 class CallableTest2 implements Callable<String>
    55 {
    56     private String id;
    57     
    58     public CallableTest2(String id)
    59     {
    60         this.id = id;
    61     }
    62 
    63     // 具体的业务逻辑;一旦CallableTest2实例对象传给ExecutorService的submit方法, 则该方法(任务)自动在一个线程上执行
    64     @Override
    65     public String call() throws Exception
    66     {
    67         System.out.println("call()方法被自动调用,当前线程名称" + Thread.currentThread().getName());
    68         return "call()方法被自动调用,任务的返回值:任务id:" + id + ",执行任务的线程名称:" + Thread.currentThread().getName();
    69     }
    70     
    71 }
    Executor执行Callable任务

    3、三种生成常用线程池的静态工厂方法(三种自带的线程池):
      newSingleThreadExecutor(SingleThreadExecutor);
      newCachedThreadPool(CachedThreadPool);
      newFixedThreadPool(FixedThreadPool);
    4、newSingleThreadExecutor的使用:
      创建一个单线程的线程池,这个线程池只有一个线程在工作,也就是相当于单线程串行执行所有任务;如果这个唯一的线程因为异常结束,那么会有一个新的线程来替代它;此线程池保证所有任务的执行顺序按照任务的提交顺序执行;
      适用场景:适用于需要保证顺序地执行各个任务;并且在任意时间点,不会有多个线程是活动的应用场景;

     1 /**
     2  * newSingleThreadExecutor的使用
     3  */
     4 package thread05;
     5 
     6 import java.util.concurrent.ExecutorService;
     7 import java.util.concurrent.Executors;
     8 
     9 public class NewSingleThreadExecutorTest01
    10 {
    11     public static void main(String[] args)
    12     {
    13         ExecutorService executorService = Executors.newSingleThreadExecutor();
    14         
    15         for(int i=0;i<5;i++)
    16         {
    17             final int no = i;
    18             
    19             // 生成一条任务
    20             Runnable runnable = new Runnable()
    21             {
    22                 @Override
    23                 public void run()
    24                 {
    25                     try
    26                     {
    27                         System.out.println("into:" + no);
    28                         Thread.sleep(1000);
    29                         System.out.println("end:" + no);
    30                     } 
    31                     catch (InterruptedException e)
    32                     {
    33                         e.printStackTrace();
    34                     }
    35                 }
    36             };
    37             
    38             // 将任务放到线程池中进行执行
    39             executorService.execute(runnable);
    40         }
    41         
    42         // 所有任务执行完毕之后,关闭线程池
    43         executorService.shutdown();
    44         
    45         System.out.println("Thread Main End!");
    46     }
    47 }
    newSingleThreadExecutor的使用

    5、newCachedThreadPool的使用:
      创建一个缓存池大小可根据需要伸缩的线程池,但是在以前构造的线程可用时可重用它们;对于执行很多短期异步任务的程序而言,这些线程池通常可提高程序性能;调用execute将重用以前构造的线程(如果线程可用);如果现有线程没有可用的,则创建一个新线程并添加到池中;终止并从缓存中移除那些已有60s未被使用的线程;因此,长时间保持空闲的线程池不会使用任何资源;
      适用场景:是大小无界的线程池,适用于执行很多的短期异步任务的小程序,或者是负载较轻的服务器;

     1 /**
     2  * newCachedThreadPool的使用
     3  */
     4 package thread05;
     5 
     6 import java.util.concurrent.ExecutorService;
     7 import java.util.concurrent.Executors;
     8 
     9 public class NewCachedThreadPoolTest01
    10 {
    11     public static void main(String[] args)
    12     {
    13         ExecutorService executorService = Executors.newCachedThreadPool();
    14         
    15         for(int i=0;i<10;i++)
    16         {
    17             final int no = i;
    18             
    19             Runnable runnable = new Runnable()
    20             {
    21                 @Override
    22                 public void run()
    23                 {
    24                     try
    25                     {
    26                         System.out.println("into:" + no);
    27                         Thread.sleep(1000L);
    28                         System.out.println("end:" + no);
    29                     } 
    30                     catch (InterruptedException e)
    31                     {
    32                         e.printStackTrace();
    33                     }
    34                 }
    35             };
    36             
    37             executorService.execute(runnable);
    38         }
    39         
    40         executorService.shutdown();
    41         
    42         System.out.println("Thread Main End!");
    43     }
    44 }
    newCachedThreadPool的使用

    6、newFixedThreadPool的使用:
      创建一个可重用固定线程数的线程池,以共享的无界队列方式来运行这些线程;在任意点,在大多数nThreads线程会处于处理任务的活动状态;如果在所有线程处于活动状态时提交附加任务,则在有可用线程之前,附加任务将在队列中等待;如果在关闭前的执行期间而导致任何线程终止,那么一个新线程将代替它执行后续的任务(如果需要);在某个线程被显式地关闭之前,池中的线程将一直存在;
      适用场景:适用于为了满足资源管理的需求,而需要限制当前线程数量的应用场景;适用于负载比较重的服务器;

     1 /**
     2  * newFixedThreadPool的使用
     3  */
     4 package thread05;
     5 
     6 import java.util.concurrent.ExecutorService;
     7 import java.util.concurrent.Executors;
     8 
     9 public class NewFixedThreadPool
    10 {
    11     public static void main(String[] args)
    12     {
    13         ExecutorService executorService = Executors.newFixedThreadPool(5);
    14         
    15         for(int i=0;i<20;i++)
    16         {
    17             final int no = i;
    18             
    19             Runnable runnable = new Runnable()
    20             {
    21                 @Override
    22                 public void run()
    23                 {
    24                     try
    25                     {
    26                         System.out.println("into:" + no);
    27                         Thread.sleep(1000L);
    28                         System.out.println("end:" + no);
    29                     } 
    30                     catch (InterruptedException e)
    31                     {
    32                         e.printStackTrace();
    33                     }
    34                 }
    35             };
    36             
    37             executorService.execute(runnable);
    38         }
    39         
    40         executorService.shutdown();
    41         
    42         System.out.println("Thread Main End!");
    43     }
    44 }
    newFixedThreadPool的使用

    7、线程池的好处;
    7.1、合理利用线程池能够带来4个好处:
    (1)、降低资源消耗:通过重复利用已创建的线程,降低线程创建和销毁造成的消耗;
    (2)、提高响应速度:当任务到达时,任务可以不需要等待线程创建就能立即执行;
    (3)、提高线程的可管理性:线程是稀缺资源,如果无限制地创建,不仅会消耗系统资源,还会降低系统的稳定性,使用线程池可以进行统一的分配、调优和监控;
    (4)、防止服务器过载,形成内存溢出,或者CPU耗尽;
    7.2、线程池技术提高服务器程序的性能:
    多线程技术主要解决处理器单元内多个线程执行的问题,它可以显著减少处理器单元的闲置时间,增加处理器单元的吞吐能力;但如果对多线程应用不当,会增加对单个任务的处理时间;
    7.3、线程池的应用范围:
    (1)、需要大量的线程来完成任务,且完成任务的时间比较短:Web服务器完成网页请求这样的任务,使用线程池技术是非常合适的;因为单个任务小,而任务数量巨大;
    8、线程池的工作机制及其原理:
    9、自定义线程池和ExecutorService:

     1 /**
     2  * 自定义线程池
     3  */
     4 package thread05;
     5 
     6 import java.util.concurrent.ArrayBlockingQueue;
     7 import java.util.concurrent.BlockingQueue;
     8 import java.util.concurrent.ThreadPoolExecutor;
     9 import java.util.concurrent.TimeUnit;
    10 
    11 public class ThreadPoolTest01
    12 {
    13     public static void main(String[] args)
    14     {
    15         // 创建等待队列 
    16         BlockingQueue<Runnable> bq = new ArrayBlockingQueue<Runnable>(20);
    17         // 创建线程池,池中保存的线程数为3,允许的最大线程数为5  
    18         ThreadPoolExecutor pool = new ThreadPoolExecutor(3, 5, 50, TimeUnit.MILLISECONDS,bq);
    19         
    20         // 创建七个任务
    21         Runnable r1 = new RunnableTest3();
    22         Runnable r2 = new RunnableTest3();
    23         Runnable r3 = new RunnableTest3();
    24         Runnable r4 = new RunnableTest3();
    25         Runnable r5 = new RunnableTest3();
    26         Runnable r6 = new RunnableTest3();
    27         Runnable r7 = new RunnableTest3();
    28         
    29         // 每个任务会在一个线程上执行
    30         pool.execute(r1);
    31         pool.execute(r2);
    32         pool.execute(r3);
    33         pool.execute(r4);
    34         pool.execute(r5);
    35         pool.execute(r6);
    36         pool.execute(r7);
    37         
    38         // 关闭线程池 
    39         pool.shutdown();
    40     }
    41 }
    42 
    43 class RunnableTest3 implements Runnable
    44 {
    45     @Override
    46     public void run()
    47     {
    48         System.out.println(Thread.currentThread().getName() + "正在执行...");
    49         
    50         try
    51         {
    52             Thread.sleep(1000);
    53         }
    54         catch (InterruptedException e)
    55         {
    56             e.printStackTrace();
    57         }
    58     }
    59     
    60 }
    自定义线程池

    10、线程池在工作中的错误使用:
    (1)、分不清线程池是单例还是多对象:线程池一定要在合理的单例模式下才有效;就是说不要多次创建线程池;
    (2)、线程池数量设置很大,请求过载:
    (3)、注意死锁问题:

  • 相关阅读:
    Linux下端口被占用,关掉端口占用的方法
    对于STM32F103的USART的通讯调试
    第一次本地代码提交到github
    SPI的学习和ESP8266的SPI通讯测试
    ubuntu下minicom安装和简单设置使用
    ubuntu18.04下stlink的一种安装方法
    使用arm-none-eabi-gdb报错error while loading shared libraries: libncurses.so.5: cannot open shared object file: No such file or directory
    在麦芒5手机播放音乐时APPS连上后出现自动重启了(请查找:REBOOT关键字)
    一连上蓝牙后,按音量加键,死机(有LOG)
    启动后,只连上蓝牙后,播放音乐,这时按音量,播放,暂停都没功能(按键是有作用的)
  • 原文地址:https://www.cnblogs.com/kehuaihan/p/8460286.html
Copyright © 2020-2023  润新知