ExecutorService是java自带线程池
线程池作用就是限制系统中执行线程的数量。
根据系统的环境情况,可以自动或手动设置线程数量,达到运行的最佳效果;少了浪费了系统资源,多了造成系统拥挤效率不高。用线程池控制线程数量,其他线程排队等候。一个任务执行完毕,再从队列的中取最前面的任务开始执行。若队列中没有等待进程,线程池的这一资源处于等待。当一个新任务需要运行时,如果线程池中有等待的工作线程,就可以开始运行了;否则进入等待队列。
比较重要的几个类:
ExecutorService | 真正的线程池接口。 |
---|---|
ScheduledExecutorService | 能和Timer/TimerTask类似,解决那些需要任务重复执行的问题。 |
ThreadPoolExecutor | ExecutorService的默认实现。 |
ScheduledThreadPoolExecutor | 继承ThreadPoolExecutor的ScheduledExecutorService接口实现,周期性任务调度的类实现。 |
要配置一个线程池是比较复杂的,尤其是对于线程池的原理不是很清楚的情况下,很有可能配置的线程池不是较优的,因此在Executors类里面提供了一些静态工厂,生成一些常用的线程池。
1. newSingleThreadExecutor
创建一个单线程的线程池。这个线程池只有一个线程在工作,也就是相当于单线程串行执行所有任务。如果这个唯一的线程因为异常结束,那么会有一个新的线程来替代它。此线程池保证所有任务的执行顺序按照任务的提交顺序执行。
2. newFixedThreadPool
创建固定大小的线程池。每次提交一个任务就创建一个线程,直到线程达到线程池的最大大小。线程池的大小一旦达到最大值就会保持不变,如果某个线程因为执行异常而结束,那么线程池会补充一个新线程。
3. newCachedThreadPool
创建一个可缓存的线程池。如果线程池的大小超过了处理任务所需要的线程,
那么就会回收部分空闲(60秒不执行任务)的线程,当任务数增加时,此线程池又可以智能的添加新线程来处理任务。此线程池不会对线程池大小做限制,线程池大小完全依赖于操作系统(或者说JVM)能够创建的最大线程大小。
4. newScheduledThreadPool
创建一个大小无限的线程池。此线程池支持定时以及周期性执行任务的需求。
详见:http://www.oschina.net/question/565065_86540
线程安全与非线程安全的类:
...
List<String> list = new ArrayList<String>();
list.add("A");
list.add("B");
list.add("C");
list = Collections.synchronizedList(list);//将ArrayList转换为线程安全的集合
System.out.println(list);//[A,B,C] 可以看出,原集合中的元素也得以保留
...
当一个程序中若创建大量线程,并在任务结束后销毁,会给系统带来过度消耗资源,以及过度切换线程的危险,从而可能导致系统崩溃。为此我们应使用线程池来解决这个问题。
ExecutorService是java提供的用于管理线程池的类。
线程池有两个主要作用:
线程池的概念:首先创建一些线程,它们的集合称为线程池,当服务器接受到一个客户请求后,就从线程池中取出一个空闲的线程为之服务,服务完后不关闭该线程,而是将该线程还回到线程池中。
在线程池的编程模式下,任务是提交给整个线程池,而不是直接交给某个线程,线程池在拿到任务后,它就在内部找有无空闲的线程,再把任务交给内部某个空闲的线程,任务是提交给整个线程池,一个线程同时只能执行一个任务,但可以同时向一个线程池提交多个任务
线程池有以下几种实现策略:
- Executors.newCachedThreadPool()
创建一个可根据需要创建新线程的线程池,但是在以前构造的线程可用时将重用它们。
- Executors.newFixedThreadPool(int nThreads)
创建一个可重用固定线程集合的线程池,以共享的无界队列方式来运行这些线程。
- Executors.newScheduledThreadPool(int corePoolSize)
创建一个线程池,它可安排在给定延迟后运行命令或者定期地执行。
- Executors.newSingleThreadExecutor()
创建一个使用单个 worker 线程的 Executor,以无界队列方式来运行该线程。
可以根据实际需求来使用某种线程池。例如,创建一个有固定线程数量的线程池:
...
ExecutorService threadPool
= Executors.newFixedThreadPool(30);//创建具有30个线程的线程池
Runnable r1 = new Runable(){
public void run(){
//线程体
}
};
threadPool.execute(r1);//将任务交给线程池,其会分配空闲线程来运行这个任务。
...
BlockingQueue是双缓冲队列。
在多线程并发时,若需要使用队列,我们可以使用Queue,但是要解决一个问题就是同步,但同步操作会降低并发对Queue操作的效率。
BlockingQueue内部使用两条队列,可允许两个线程同时向队列一个做存储,一个做取出操作。在保证并发安全的同时提高了队列的存取效率。
双缓冲队列有一下几种实现:
- ArrayBlockingDeque:规定大小的BlockingDeque,其构造函数必须带一个int参数来指明其大小.其所含的对象是以FIFO(先入先出)顺序排序的。
- LinkedBlockingDeque:大小不定的BlockingDeque,若其构造函数带一个规定大小的参数,生成的BlockingDeque有大小限制,若不带大小参数,所生成的BlockingDeque的大小由Integer.MAX_VALUE来决定.其所含的对象是以FIFO(先入先出)顺序排序的。
- PriorityBlockingDeque:类似于LinkedBlockDeque,但其所含对象的排序不是FIFO,而是依据对象的自然排序顺序或者是构造函数的Comparator决定的顺序。
- SynchronousQueue:特殊的BlockingQueue,对其的操作必须是放和取交替完成的。
例如:
public static void main(String[] args) {
BlockingQueue<String> queue
= new LinkedBlockingDeque<String>();
try {
//queue.offer("A");//立即向队列末尾追加元素
/* * 向队列末尾追加元素,指定可延迟5秒。 * 若5秒钟内成功将元素加入队列返回true * 若超时后元素仍然没有加入队列则返回flase */
queue.offer("A",5,TimeUnit.SECONDS);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(queue.poll());
}
详细要求如下:
1. 线程池要执行的任务为每隔一秒输出一次当前线程的名字,总计输出10次。
2. 创建一个线程池,该线程池中只有两个空线程。
3. 使线程池执行5次步骤一的任务。
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/** * 测试线程池 */
public class TestExecutorService {
public static void main(String[] args) {
ExecutorService threadPool = Executors.newFixedThreadPool(2);
for(int i=0;i<5;i++){
Handler handler = new Handler();
threadPool.execute(handler);
}
}
}
class Handler implements Runnable{
public void run(){
String name = Thread.currentThread().getName();
System.out.println("执行当前任务的线程为:"+name);
for(int i=0;i<10;i++){
System.out.println(name+":"+i);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(name+":任务完毕");
}
}
详细要求如下:
1. 首先,使用ArrayBlockingQueue类创建一个大小为10的双缓冲队列queue;然后,循环20次向队列queue中添加元素,如果5秒内元素仍没有入队到队列中,则返回false。
2. 首先,使用ArrayBlockingQueue类创建一个大小为10的双缓冲队列queue;然后,将0到9,10个数字加入到队列queue中;最后,循环20次从队列queue中取元素,如果5秒内还没有元素可取出则返回null。
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.TimeUnit;
import org.junit.Test;
/** * 测试双缓冲队列 */
public class TestBlockingQueue {
/** * 测试入队方法 */
@Test
public void testOffer() {
BlockingQueue<Integer> queue
= new ArrayBlockingQueue<Integer>(10);
for(int i=0;i<20;i++){
try {
//设置5秒超时,5秒内元素仍没有入队,则返回false
boolean b = queue.offer(i,5,TimeUnit.SECONDS);
System.out.println("存入是否成功:"+b);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
/** * 测试出队方法 */
@Test
public void testPull() {
BlockingQueue<Integer> queue
= new ArrayBlockingQueue<Integer>(10);
for(int i=0;i<10;i++){
queue.offer(i);
}
for(int i =0;i<20;i++){
//获取元素,设置5秒超时,5秒内还没有元素可取出则返回null
try {
Integer num = queue.poll(5, TimeUnit.SECONDS);
System.out.println("元素:"+num);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}