最近在写代码的时候需要模拟多个用户并发请求的场景,这种时候借助于多线程来实现是个很好的办法。而利用线程池来管理和实现多线程是目前比较流行和高效的做法了。
什么是线程池?
我的理解是,线程池就像一个池子,里面有好多的线程。当我们有任务要执行的时候,就从池子里面拿出一个线程来执行,执行完毕之后就把线程放回到池子里。当然如果此时池子里没有线程,那就要等待了。
为什么要用线程池?
通过上面的描述,其实很容易得出使用线程池的好处,主要有三点:第一,降低系统消耗,线程可以重复使用,避免资源反复创建销毁。第二,减少任务等待,线程池中的线程是已经创建好的,任务创建后直接就可以支持,减少了等待线程创建的时间。第三,提高线程可管理性,因为线程都在一个池子里,可以统一的进行分配,监控,管理等。
怎么使用?
说了线程池的好,那就要说说具体该怎么使用了。Java提供了Executors类来创建线程池。能够创建线程池主要有以下几种:
- newFixedThreadPool,创建指定线程个数的线程池,也就是说如果任务数比线程数多了,那任务就只能在一个队列中排队等候了。
- newSingleThreadExecutor,顾名思义就是创建单个线程,所有的任务按照FIFO的顺序执行。
- newCachedThreadPool,这是创建一个可以动态变化大小的线程池,这个就比较智能的感觉,当线程池中线程不够用的时候可以主动创建线程,然后当线程空闲一段时间后,可以主动回收线程。
- newScheduledThreadPool,创建一个周期执行任务的线程池。
- newWorkStealingPool,JAVA 8中新加的一种,它创建和当前机器可用处理器个数等量的线程数。
实际编码中,大同小异这里以newFixedThreadPool为例,示例代码如下:
先创建一个Runable类说明线程执行的任务内容:
static class Work implements Runnable { private int workNumber; public Work (int n) { this.workNumber = n; } @Override public void run() { System.out.println(Thread.currentThread().getName() + " Start, " + "Work Number: " + this.workNumber); try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + " Done, " + "Work Number: " + this.workNumber); } }
再创建线程池来执行任务:
public static void main(String[] args) { ExecutorService executor = Executors.newFixedThreadPool(4); for (int i = 0; i < 10; i++) { executor.execute(new Work(i)); } executor.shutdown(); try { executor.awaitTermination(Long.MAX_VALUE, TimeUnit.NANOSECONDS); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("All done."); }
需要注意的就是在提交完任务给线程池后,如果想要等所有线程都执行完就要先调用shutdown(),再调用awaitTermination()即可。
下面就是程序运行的结果:
OK,对于线程池的简单使用到这里就可以了。当然根据需求的不同可以对线程池做各种定制修改,可以参考官方文档。这里就不多说了,看以后有没有机会碰到吧。