java concurrent线程池_Java中java.util.concurrent包下的4中线程池代码示例

先来看下ThreadPool的类结构

3a68043034e0980caffab92195535f07.png

其中红色框住的是常用的接口和类(图片来自:https://blog.csdn.net/panweiwei1994/article/details/78617117?from=singlemessage)

为什么需要线程池呢?

我们在创建线程的时候,一般使用new Thread(),但是每次在启动一个线程的时候就new 一个Thread对象,会让性能变差(spring不都使用IOC管理对象了嘛)。还有其他的一些弊端:

可能会造成无限创建线程对象,对象之间相互竞争资源,造成过多占用资源而宕机。

缺乏相关功能,如定时执行、定期执行、线程中断。

使用线程池的避免这些事情:

重用存在的线程,减少对象创建、消亡的开销,性能佳。

可有效控制最大并发线程数,提高系统资源的使用率,同时避免过多资源竞争,避免堵塞。

提供定时执行、定期执行、单线程、并发数控制等功能。

线程池的种类:

java通过Executor是提供4种线程池,分别为:

1)newCachedThreadPool:创建一个可缓存的线程池,有任务来临时如果线程池中有空闲的线程,那么就使用空闲的线程执行任务(即线程是可以复用),如果没有空闲线程则创建新的线程执行任务。

2)newFixedThreadPool:创建一个定长的线程池,线程池的线程数量固定,当任务来临,但是又没有空闲线程,则把任务放入队列中等待直到有空闲线程来处理它。

3)newScheduledThreadPool:创建一个定长的线程,但是能支持定时或周期性的执行。

4)newSingleThreadPool:创建一个单线程化的线程池,线程池中只有一个唯一的线程来执行任务,保证所有任务按照指定顺序(FIFO,LIFO,优先级)执行。

示例:

1)newCachedThreadPool

1 /**

2 * 创建可缓存的线程池,线程池的线程可以重复利用,除非任务来不及处理就会创建新的线程。3 */

4 public static voidcreateCachedThreadPool(){5 ExecutorService cachedThreadPool =Executors.newCachedThreadPool();6 for (int i = 0; i < 10; i++) {7 final int index =i;8 try{9 //在这里使主线程停下来,让启动的线程执行完Syso操作10 //并且有时间回收线程以确保下次启动的线程还是上次的线程

11 Thread.sleep(index * 1000);//

12 } catch(InterruptedException e) {13 e.printStackTrace();14 }15 cachedThreadPool.execute(newRunnable() {16 @Override17 public voidrun() {18 /*try {19 //这里让启动的线程睡眠,保证下次启动的线程是新的线程,不是此时睡眠的。20 Thread.sleep(index * 1000);21 } catch (InterruptedException e) {22 // TODO Auto-generated catch block23 e.printStackTrace();24 }*/

25 System.out.println(Thread.currentThread().getName() + " " +index);26 }27 });28

29 }30 }

执行结果:

297b70c9b1b8b500d4948545c21c9d98.png

结果显示,执行for循环输出的线程都是同一个,线程重复使用了。

把注释的地方放开,并且注释上面的睡眠,执行结果:

af7065ccb8141b375438769dd26737c2.png

结果显示的是不同的线程名称执行的for循环,对比上面的执行结果的线程名称,可以得出结论:有任务来临时如果线程池中有空闲的线程,那么就使用空闲的线程执行任务(即线程是可以复用),如果没有空闲线程则创建新的线程执行任务。

2)newFixedThreadPool

1 /**

2 * 创建固定长度的线程池,超出的任务会在队列中进行等待,直到有线程空出来来执行。3 */

4 public static voidcreateFixedThreadPool() {5 ExecutorService fixedThreadPool = Executors.newFixedThreadPool(3);6 for (int i = 0; i < 10; i++) {7 final int index =i;8 fixedThreadPool.execute(newRunnable() {9 @Override10 public voidrun() {11 try{12 System.out.println(Thread.currentThread().getName() + "," +index);13 Thread.sleep(2000);14 } catch(InterruptedException e) {15 e.printStackTrace();16 }17 }18 });19 }20 }

运行结果:

6c0b511718a252126e7c2249ce354e1c.png

创建了固定长度是3的线程池,输出前3行之后,发现线程都在sleep(),要执行的输出任务没有找到对应的执行线程,任务就会放入队列中进行等待,等待某个线程执行完毕后,再去执行任务。(线程池中的线程也是重复使用的)

3)newScheduledThreadPool

3.1)延迟执行某个线程

1 public static voidcreateScheduledThreadPoolToDelay(){2 ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(5);3 for(int i = 0; i < 10; i++){4 final int index =i;5 try{6 Thread.sleep(2000);7 } catch(InterruptedException e) {8 e.printStackTrace();9 }10 scheduledThreadPool.schedule(newRunnable(){11 @Override12 public voidrun() {13 System.out.println(Thread.currentThread().getName() + " " + index + " delay 3 seconds");14 }15 }, 3, TimeUnit.SECONDS);16 }17 }

执行结果:

372f8db8ba8c50a9bbafe904a6eb0803.png

延迟3+2秒执行(3秒是newScheduledThreadPool中设置的,2秒是Thread.sleep()设置的),结果中可以看出主线程睡眠2秒并不能保证newScheduledThreadPool线程池中是使用旧线程执行任务还是新建线程执行任务,这种情况是随机的。(这点和newCachedThreadPool不一样,newCachedThreadPool是用就线程)

3.2)定期执行某个任务

1 public static voidcreateScheduledThreadPoolToFixRate(){2 ScheduledExecutorService exe = Executors.newScheduledThreadPool(3);3 exe.scheduleAtFixedRate(newRunnable(){4 @Override5 public voidrun() {6 System.out.println(Thread.currentThread().getName() + " delay 1 seconds, and excute every 3 seconds");7 }8 }, 1, 3, TimeUnit.SECONDS);9

10 }

执行结果:

7f98f2487a6e63dd46ecd478821fc276.png

结果是延迟1s启动线程,并且之后每隔3s重复执行任务,但是用的是同一个线程。

4)newSingleThreadPool

1 /**

2 * 创建一个单线程的线程池3 */

4 public static voidcreateSingleThreadPool(){5 ExecutorService exe =Executors.newSingleThreadExecutor();6 for(int i = 0; i < 10; i++){7 final int index =i;8 exe.execute(newRunnable() {9 @Override10 public voidrun() {11 try{12 System.out.println(Thread.currentThread().getName() + ", " +index);13 Thread.sleep(2000);//让当前线程睡眠2s,发现顺序打印1~10,并且有个打印停顿2s

14 } catch(InterruptedException e) {15 e.printStackTrace();16 }17 }18 });19 }20 }

运行结果:

709b0145bd51c187b9ee16a30dff3264.png

每输出一行结果就等待2s,可以看出每次输出的线程名都一样,说使用的同一个线程。单线程化的线程池中只有一个线程。

总结:

上面就是4中线程池的实现及其使用示例,和他们之间的区别。

其中newFixedThreadPool()有个坑,最好不要使用Executor.newFixedThreadPool(int nThreads)来创建线程池,因为它使用了LinkedBlockingQueue,容量是Integer.MAX_VALUE,容量太大容易造成防止所有任务都被阻塞,从而导致死锁。下面是具体源码:

public static ExecutorService newFixedThreadPool(intnThreads) {return newThreadPoolExecutor(nThreads, nThreads,0L, TimeUnit.MILLISECONDS,new LinkedBlockingQueue());

}

/*** Creates a LinkedBlockingQueue with a capacity of

* {@linkInteger#MAX_VALUE}.*/

publicLinkedBlockingQueue() {this(Integer.MAX_VALUE);

}

应该尽量直接使用new ThreadPoolExecutor来创建线程池,并指定阻塞队列的容量。

参考文章:https://www.cnblogs.com/zhaoyan001/p/7049627.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值