【Java线程:线程池】

线程池的基本思想还是一种对象池的思想,开辟一块内存空间,里面存放了众多(未死亡)的线程,池中线程执行调度由池管理器来处理。当有线程任务时,从池中取一个,执行完成后线程对象归池,这样可以避免反复创建线程对象所带来的性能开销,节省了系统的资源。

在Java5之前,要实现一个线程池是相当有难度的,现在我们只需要按照提供的API来使用,即可享受线程池带来的极大便利。

在使用线程池之前,必须知道如何去创建一个线程池,在Java5中,需要了解的是java.util.concurrent.Executors类的API,这个类提供大量创建连接池的静态方法,是必须掌握的。

一、固定大小的线程池

java 线程池设置为守护线程 java 固定线程池_java 线程池设置为守护线程

运行结果:

java 线程池设置为守护线程 java 固定线程池_java 线程池设置为守护线程_02

二、单任务线程池

在上例的基础上改一行创建pool对象的代码为:

java 线程池设置为守护线程 java 固定线程池_java 线程池设置为守护线程_03

运行结果:

java 线程池设置为守护线程 java 固定线程池_连接池_04

对于以上两种连接池,大小都是固定的,当要加入的池的线程(或者任务)超过池最大尺寸时候,则入此线程池需要排队等待。

一旦池中有线程完毕,则排队等待的某个线程会入池执行。

三、可变尺寸的线程池

与上面的类似,只是改动下pool的创建方式:

java 线程池设置为守护线程 java 固定线程池_java 线程池设置为守护线程_05

运行结果:

java 线程池设置为守护线程 java 固定线程池_java 保持固定接数_06

四、延迟连接池

java 线程池设置为守护线程 java 固定线程池_连接池_07

运行结果:

java 线程池设置为守护线程 java 固定线程池_Java_08

五、单任务延迟连接池

在上面代码基础上,做改动

java 线程池设置为守护线程 java 固定线程池_java 线程池设置为守护线程_09

运行结果:

java 线程池设置为守护线程 java 固定线程池_连接池_04

六、自定义线程池

java 线程池设置为守护线程 java 固定线程池_线程池_11

运行结果:

java 线程池设置为守护线程 java 固定线程池_java 保持固定接数_12

创建自定义线程池的构造方法很多,本例中参数的含义如下:

ThreadPoolExecutor

java 线程池设置为守护线程 java 固定线程池_java 保持固定接数_13

参数:

corePoolSize -池中所保存的线程数,包括空闲线程。

maximumPoolSize -池中允许的最大线程数。

keepAliveTime -当线程数大于核心时,此为终止前多余的空闲线程等待新任务的最长时间。

unit - keepAliveTime参数的时间单位。

workQueue -执行前用于保持任务的队列。此队列仅保持由execute方法提交的Runnable任务。

抛出:

IllegalArgumentException-如果 corePoolSize或 keepAliveTime小于零,或者 maximumPoolSize小于或等于零,或者 corePoolSize大于 maximumPoolSize。

NullPointerException -如果workQueue为 null

自定义连接池稍微麻烦些,不过通过创建的ThreadPoolExecutor线程池对象,可以获取到当前线程池的尺寸、正在执行任务的线程数、工作队列等等。

【Java线程:有返回值的线程】

在Java5之前,线程是没有返回值的,常常为了“有”返回值,破费周折,而且代码很不好写。或者干脆绕过这道坎,走别的路了。

现在Java终于有可返回值的任务(也可以叫做线程)了。

可返回值的任务必须实现Callable接口,类似的,无返回值的任务必须Runnable接口。

执行Callable任务后,可以获取一个Future的对象,在该对象上调用get就可以获取到Callable任务返回的Object了。

下面是个很简单的例子:

java 线程池设置为守护线程 java 固定线程池_java 保持固定接数_14

运行结果:

java 线程池设置为守护线程 java 固定线程池_java 保持固定接数_15

【Java线程:锁(上)】

利用锁可以方便的实现资源的封锁,用来控制对竞争资源并发访问的控制,这些内容主要集中在java.util.concurrent.locks包下面,里面有三个重要的接口Condition、Lock、ReadWriteLock。

java 线程池设置为守护线程 java 固定线程池_线程池_16

看个例子:

java 线程池设置为守护线程 java 固定线程池_连接池_17

运行结果:

java 线程池设置为守护线程 java 固定线程池_Java_18

从上面的输出可以看到,利用锁对象太方便了,比直接在某个不知情的对象上用锁清晰多了。

但一定要注意的是,在获取了锁对象后,用完后应该尽快释放锁,以便别的等待该锁的线程有机会去执行。

【Java线程:锁(下)】

在上文中提到了Lock接口以及对象,使用它,很优雅的控制了竞争资源的安全访问,但是这种锁不区分读写,称这种锁为普通锁。为了提高性能,Java提供了读写锁,在读的地方使用读锁,在写的地方使用写锁,灵活控制,在一定程度上提高了程序的执行效率。

Java中读写锁有个接口java.util.concurrent.locks.ReadWriteLock,也有具体的实现ReentrantReadWriteLock,详细的API可以查看JavaAPI文档。

下面这个例子是在上文例子的基础上,将普通锁改为读写锁,并添加账户余额查询的功能,代码如下:

java 线程池设置为守护线程 java 固定线程池_线程池_19

运行结果:

java 线程池设置为守护线程 java 固定线程池_java 保持固定接数_20

在实际开发中,最好在能用读写锁的情况下使用读写锁,而不要用普通锁,以求更好的性能。