Java并发编程从入门到精通 - 第6章:线程池

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)、注意死锁问题:

原文地址:https://www.cnblogs.com/kehuaihan/p/8460286.html

时间: 2024-11-07 17:17:18

Java并发编程从入门到精通 - 第6章:线程池的相关文章

Java并发编程从入门到精通 - 第5章:多线程之间的交互:线程阀

详述: 线程阀是一种线程与线程之间相互制约和交互的机制: 作用:http://wsmajunfeng.iteye.com/blog/1629354阻塞队列BlockingQueue:数组阻塞队列ArrayBlockingQueue:链表阻塞队列LinkedBlockingQueue:优先级阻塞队列PriorityBlockingQueue:延时队列DelayQueue:同步队列SynchronousQueue:链表双向阻塞队列LinkedBlockingDeque:链表传输队列LinkedTra

Java并发编程从入门到精通 - 第2章:认识Thread

线程实现的三种方法:1.三种实现方式的简记: 继承Thread类,重写run()方法: 实现Runnable接口,重写run()方法,子类创建对象并作为Thread类的构造器参数: 实现Callable接口,重写call()方法,子类创建对象并作为FutureTask类的构造器参数,FutureTask类创建对象并作为Thread类的构造器参数:2.三种实现方法的比较: 继承Thread类:因为是单继承,所以扩展性不好: 实现Runnable接口:接口可以多重实现:并且还可以再继承一个类:扩展性

Java并发编程从入门到精通 - 第3章:Thread安全

Java内存模型与多线程: 线程不安全与线程安全: 线程安全问题阐述:  多条语句操作多个线程共享的资源时,一个线程只执行了部分语句,还没执行完,另一个线程又进来操作共享数据(执行语句),导致共享数据最终结果出现误差:所以就是看一个线程能否每次在没有其他线程进入的情况下操作完包含共享资源的语句块,如果能就没有安全问题,不能就有安全问题: 如何模拟多线程的安全问题:  用Thread.sleep()方法模拟: 放在哪:放在多线程操作共享数据的语句块之间(使正在运行的线程休息一会,让其他线程执行,就

Java并发编程从入门到精通 - 第7章:Fork/Join框架

1.综述:化繁为简,分而治之:递归的分解和合并,直到任务小到可以接受的程度:2.Future任务机制:  Future接口就是对于具体的Runnable或者Callable任务的执行结果进行取消.查询是否完成.获取结果:必要时可以通过get方法获取执行结果,该方法会阻塞直到任务会返回结果:也就是说Future接口提供三种功能:判断任务是否完成.能够中断任务.能够获取任务执行结果:  Future接口里面的常用方法:3.FutureTask:  FutureTask类是Future接口唯一的实现类

Java并发编程从入门到精通 张振华.Jack --我的书

[当当.京东.天猫.亚马逊.新华书店等均有销售] 目 录 第一部分:线程并发基础 第1章 概念部分   1 1.1 CPU核心数.线程数 (主流cpu,线程数的大体情况说一下) 1 1.2 CPU时间片轮转机制 2 1.3 什么是进程和什么是线程 4 1.4 进程和线程的比较 5 1.5 什么是并行运行 7 1.6 什么是多并发运行 8 1.7 什么是吞吐量 9 1.8  多并发编程的意义及其好处和注意事项 10 1.9  分布式与并发运算关系 11 1.10 Linux和Window多并发可以

Java并发编程从入门到精通 张振华.Jack --【吐血推荐、热销书籍】

[当当.京东.天猫.亚马逊.新华书店等均有销售]目 录 第一部分:线程并发基础 第1章 概念部分   1 1.1 CPU核心数.线程数 (主流cpu,线程数的大体情况说一下) 1 1.2 CPU时间片轮转机制 2 1.3 什么是进程和什么是线程 4 1.4 进程和线程的比较 5 1.5 什么是并行运行 7 1.6 什么是多并发运行 8 1.7 什么是吞吐量 9 1.8  多并发编程的意义及其好处和注意事项 10 1.9  分布式与并发运算关系 11 1.10 Linux和Window多并发可以采

Java并发编程从入门到精通-总纲

总纲: Thread; Thread安全; 线程安全的集合类; 多线程之间交互:线程阀; 线程池; Fork/Join; 第2章:认识Thread: 线程实现的三种方法; Thread里面的属性和方法; 线程的中断机制; 线程的生命周期; 守护线程; 线程组; 当前线程的副本:ThreadLocal; 线程异常的处理; 第3章:Thread安全: Java内存模型与多线程: 线程不安全和线程安全: 隐式锁synchronized: 显式锁Lock和ReentrantLock: 显式锁ReadWr

java并发的艺术-读书笔记-第九章线程池

使用线程池的好处: 1.降低资源消耗:减少了线程创建和销毁的资源消耗 2.提高响应速度,当任务到达时,线程可以不尽兴创建直接处理 3.提高线程的可管理性.使用线程池可以对线程进行统一的管理,监控,使用. 线程池的源码分析: public void execute(Runnable command){ if(command==null){ throw new NullPointerException(); } //如果执行线程数小于基本线程,则创建线程,并执行任务 if(poolsize>=cor

Java网络编程从入门到精通(4):DNS缓存

在通过DNS查找域名的过程中,可能会经过多台中间DNS服务器才能找到指定的域名,因此,在DNS服务器上查找域名是非常昂贵的操作.在Java中为了缓解这个问题,提供了DNS缓存.当InetAddress类第一次使用某个域名(如www.csdn.net)创建InetAddress对象后,JVM就会将这个域名和它从DNS上获得的信息(如IP地址)都保存在DNS缓存中.当下一次InetAddress类再使用这个域名时,就直接从DNS缓存里获得所需的信息,而无需再访问DNS服务器. DNS缓存在默认时将永