最近工作上需要做一个大数据量的读写操作,为了实现高效率,想到了用多线程实现。在网上查一些资料,发现在JDK 1.5之后有了一个非常方便的东东---Executor来实现多线程。
从JDK 1.5开始,增加了java.util.concurrent包,它的引入大大简化了多线程程序的开发。
java.util.concurrent包分成了三个部分,分别是java.util.concurrent、
java.util.concurrent.atomic和java.util.concurrent.lock。内容涵盖了并发集合类、线程池机制、同步互斥机制、线程安全的变量更新工具类、锁等等常用工具。
在稍老一些的Java版本中,具体是在Java
5.0以前,启动一个任务是通过调用Thread类的start()方法来实现的,任务的提交和执行时同时进行的,如果想对任务的执行进行调度,或者想控
制同时执行的线程数量就需要额外编写代码来完成。而在Java
5.0里提供了一个新的任务执行架构使我们可以很轻松地调度和控制任务的执行。这个架构主要有三个接口和其相应的类组成。这三个接口分别是:Executor、ExevutirService和ScheduledExecutorService。下面我们来具体说明:
Executor接口:
是用来执行Runnable任务的,它值定义了一个方法:
(官方文档介绍在:http://docs.oracle.com/javase/6/docs/api/java/util/concurrent/Executor.html)
execute(Runnable command):执行Ruannable类型的任务.
ExecutorService接口:ExecutorService继承了Executor的方法,并提供了执行Callable任务和中指任务执行的服务,其定义的方法主要有:
submit(task):可用来提交Callable或Runnable任务,并返回代表此任务的Future对象
invokeAll(collection of tasks):批处理任务集合,并返回一个代表这些任务的Future对象集合
shutdown():在完成已提交的任务后关闭服务,不再接受新任务
shutdownNow():停止所有正在执行的任务并关闭服务。
isTerminated():测试是否所有任务都执行完毕了。
isShutdown():测试是否该ExecutorService已被关闭
ExecutorService接口:
ExecutorService提供了执行任务的功能,它提供的方法主要有:
(官方文档介绍在:http://docs.oracle.com/javase/6/docs/api/java/util/concurrent/ExecutorService.html)
boolean
awaitTermination(long timeout,TimeUnit unit):等待timeout设置的超时时间,如果超过了超时时间还没结束的话,就返回false,否则返回true。
ScheduledExecutorService接口:
在ExecutorService的基础上,ScheduledExecutorService提供了按时间安排执行任务的功能,它提供的方法主要有:
(官方文档介绍在:http://docs.oracle.com/javase/6/docs/api/java/util/concurrent/ScheduledExecutorService.html)
schedule(task, initDelay): 安排所提交的Callable或Runnable任务在initDelay指定的时间后执行
scheduleAtFixedRate():安排所提交的Runnable任务按指定的间隔重复执行
scheduleWithFixedDelay():安排所提交的Runnable任务在每次执行完后,等待delay所指定的时间后重复执行
它们三个都是接口,互相之间是继承关系:
ScheduledExecutorService ==继承自=> ExecutorService ==继承自=> Executor
Executors类:
虽然以上提到的接口有其实现类,但是为了方便,我们建议使用Executors的工具类来得到Executor接口的具体对象,需要注意的是,Executors是一个类,不是Executor的复数形式,Executor提供了一下一些静态方法:
(官方文档介绍在:http://docs.oracle.com/javase/6/docs/api/java/util/concurrent/Executors.html)
callable(Runnable task):将一个Runnable的任务转化成callable的任务
newSingleThreadExecutor: 产生一个ExecutorService对象,这个对象只有一个线程可用来执行任务,若任务多于一个,任务将按先后顺序执行
newCachedThreadPool(): 产生一个ExecutorService对象,这个对象带有一个线程池,线程池的大小会根据需要调整,线程执行完任务后返回线程池,供执行下一次任务使用。
实例:
newFixedThreadPool(int poolSize):产生一个ExecutorService对象,这个对象带有一个大小为poolSize的线程池,若任务数量大于poolSize,任务会被放在一个queue里顺序执行
newSingleThreadScheduledExecutor:产生一个ScheduledExecutorService对象,这个对象的线程池大小为1,若任务多于一个,任务将按先后顺序执行
newScheduledThreadPool(int poolSize): 产生一个ScheduledExecutorService对象,这个对象的线程池大小为poolSize,若任务数量大于poolSize,任务会在一个queue里等待执行
接下来我将用实例来介绍:
测试线程:
package com.yanyanblog.concurrent; public class TestThread implements Runnable{ protected int num; public TestThread() {} public TestThread(int num) { this.num= num; } @Override public void run() { System.out.print(num); } }
Executors.newCachedThreadPool();创建corePoolSize为0,最大线程数为整型的最大数,线程keepAliveTime为1分钟,缓存任务的队列为SynchronousQueue的线程池。
实现:
package com.yanyanblog.concurrent; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; public class CachedThreadPool { public static void main(String[] args) { ExecutorService exec = Executors.newCachedThreadPool(); for(int i = 0; i < 10; i++) { exec.execute(new TestThread(i)); } exec.shutdown(); } }
Executors.newFixedThreadPool(int
nThreads);创建固定大小(nThreads,大小不能超过int的最大值)的线程池
实现:
package com.yanyanblog.concurrent; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; public class FixedThreadPool { public static void main(String[] args) { //三个线程来执行五个任务 ExecutorService exec = Executors.newFixedThreadPool(3); for(int i = 0; i < 5; i++) { exec.execute(new TestThread(i)); } exec.shutdown(); } }
Executors.newSingleThreadExecutor():创建大小为1的固定线程池。
实现:
package com.yanyanblog.concurrent; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; public class SingleThreadExecutor { public static void main(String[] args) { ExecutorService exec = Executors.newSingleThreadExecutor(); for (int i = 0; i < 2; i++) { exec.execute(new TestThread(i)); } } }
第一个任务执行完了之后才开始执行第二个任务。
总结:
1、Executors
通过这个类能够获得多种线程池的实例,例如可以调用newSingleThreadExecutor()获得单线程的ExecutorService,调用newFixedThreadPool()获得固定大小线程池的ExecutorService。拿到ExecutorService可以做的事情就比
较多了,最简单的是用它来执行Runnable对象,也可以执行一些实现了Callable<t>的对象。用Thread的start()方
法没有返回值,如果该线程执行的方法有返回值那用ExecutorService就再好不过了,可以选择submit()、invokeAll()或者
invokeAny(),根据具体情况选择合适的方法即可。
2. Atomics原子级变量
原子量级的变量,主要的类有AtomicBoolean, AtomicInteger, AotmicIntegerArray,
AtomicLong, AtomicLongArray, AtomicReference ……。这些原子量级的变量主要提供两个方法:
compareAndSet(expectedValue,
newValue):比较当前的值是否等于expectedValue,若等于把当前值改成newValue,并返回true。若不等,返回false。
getAndSet(newValue):把当前值改为newValue,并返回改变前的值。
这些原子级变量利用了现代处理器(CPU)的硬件支持可把两步操作合为一步的功能,避免了不必要的锁定,提高了程序的运行效率。
Executor实现线程池