当需要有大量线程运行时,不可避免的就要反复进行创建,运行,销毁线程,然而显式的这些操作本身都是比较消耗CPU的,必须很好的将这些线程管理起来,如线程池就是一个很好的例子,java提供了许多Executor用以提供对线程的管理。
1.ThreadPoolExecutor
下面程序模拟了利用ThreadPoolExetor来执行10个任务,
public class Program{
public static void main(String[] agrs){
Server s=new Server();
for(int i=0;i<10;i++){
Task task=new Task(new Date(),"task"+i);
s.executeTask(task);
}
s.endServer();
}
}
class Task implements Runnable{
private Date initDate;
private String name;
public Task(Date initDate,String name){
this.initDate=initDate;
this.name=name;
}
@Override
public void run(){
System.out.printf("%s: Task %s: Created on: %s\n",Thread.currentThread().getName(),name,initDate);
System.out.printf("%s: Task %s: Started on: %s\n",Thread.currentThread().getName(),name,new Date());
try {
Long duration=(long)(Math.random()*10);
System.out.printf("%s: Task %s: Doing a task during %d seconds\n",Thread.currentThread().getName(),name,duration);
TimeUnit.SECONDS.sleep(duration);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.printf("%s: Task %s: Finished on: %s\n",Thread.currentThread().getName(),name,new Date());
}
}
class Server{
private ThreadPoolExecutor executor;
public Server(){
executor=(ThreadPoolExecutor)Executors.newCachedThreadPool();
}
public void executeTask(Task task){
System.out.println("a new task has arrived");
executor.execute(task);
System.out.printf("Server: Pool Size: %d Active Count: %d Completed Tasks: %d\n",executor.getPoolSize(),executor.getActiveCount(),executor.getCompletedTaskCount());
}
public void endServer(){
executor.shutdown();
}
}
执行结果如下:
a new task has arrived
pool-1-thread-1: Task task0: Created on: Wed Feb 12 14:54:12 CST 2014
pool-1-thread-1: Task task0: Started on: Wed Feb 12 14:54:12 CST 2014
Server: Pool Size: 1 Active Count: 1 Completed Tasks: 0
pool-1-thread-1: Task task0: Doing a task during 4 seconds
a new task has arrived
Server: Pool Size: 2 Active Count: 1 Completed Tasks: 0
a new task has arrived
pool-1-thread-2: Task task1: Created on: Wed Feb 12 14:54:12 CST 2014
pool-1-thread-2: Task task1: Started on: Wed Feb 12 14:54:12 CST 2014
pool-1-thread-3: Task task2: Created on: Wed Feb 12 14:54:12 CST 2014
pool-1-thread-3: Task task2: Started on: Wed Feb 12 14:54:12 CST 2014
pool-1-thread-3: Task task2: Doing a task during 1 seconds
Server: Pool Size: 3 Active Count: 2 Completed Tasks: 0
a new task has arrived
pool-1-thread-2: Task task1: Doing a task during 3 seconds
pool-1-thread-4: Task task3: Created on: Wed Feb 12 14:54:12 CST 2014
pool-1-thread-4: Task task3: Started on: Wed Feb 12 14:54:12 CST 2014
pool-1-thread-4: Task task3: Doing a task during 3 seconds
Server: Pool Size: 4 Active Count: 3 Completed Tasks: 0
a new task has arrived
Server: Pool Size: 5 Active Count: 4 Completed Tasks: 0
a new task has arrived
pool-1-thread-5: Task task4: Created on: Wed Feb 12 14:54:12 CST 2014
pool-1-thread-6: Task task5: Created on: Wed Feb 12 14:54:12 CST 2014
pool-1-thread-6: Task task5: Started on: Wed Feb 12 14:54:12 CST 2014
pool-1-thread-6: Task task5: Doing a task during 1 seconds
Server: Pool Size: 6 Active Count: 5 Completed Tasks: 0
a new task has arrived
pool-1-thread-5: Task task4: Started on: Wed Feb 12 14:54:12 CST 2014
pool-1-thread-7: Task task6: Created on: Wed Feb 12 14:54:12 CST 2014
pool-1-thread-7: Task task6: Started on: Wed Feb 12 14:54:12 CST 2014
Server: Pool Size: 7 Active Count: 6 Completed Tasks: 0
a new task has arrived
pool-1-thread-7: Task task6: Doing a task during 0 seconds
pool-1-thread-5: Task task4: Doing a task during 6 seconds
pool-1-thread-7: Task task6: Finished on: Wed Feb 12 14:54:12 CST 2014
pool-1-thread-8: Task task7: Created on: Wed Feb 12 14:54:12 CST 2014
pool-1-thread-8: Task task7: Started on: Wed Feb 12 14:54:12 CST 2014
Server: Pool Size: 8 Active Count: 7 Completed Tasks: 0
pool-1-thread-8: Task task7: Doing a task during 2 seconds
a new task has arrived
pool-1-thread-7: Task task8: Created on: Wed Feb 12 14:54:12 CST 2014
pool-1-thread-7: Task task8: Started on: Wed Feb 12 14:54:12 CST 2014
Server: Pool Size: 8 Active Count: 7 Completed Tasks: 1
a new task has arrived
pool-1-thread-7: Task task8: Doing a task during 6 seconds
Server: Pool Size: 9 Active Count: 8 Completed Tasks: 1
pool-1-thread-9: Task task9: Created on: Wed Feb 12 14:54:12 CST 2014
pool-1-thread-9: Task task9: Started on: Wed Feb 12 14:54:12 CST 2014
pool-1-thread-9: Task task9: Doing a task during 8 seconds
pool-1-thread-3: Task task2: Finished on: Wed Feb 12 14:54:13 CST 2014
pool-1-thread-6: Task task5: Finished on: Wed Feb 12 14:54:13 CST 2014
pool-1-thread-8: Task task7: Finished on: Wed Feb 12 14:54:14 CST 2014
pool-1-thread-2: Task task1: Finished on: Wed Feb 12 14:54:15 CST 2014
pool-1-thread-4: Task task3: Finished on: Wed Feb 12 14:54:15 CST 2014
pool-1-thread-1: Task task0: Finished on: Wed Feb 12 14:54:16 CST 2014
pool-1-thread-5: Task task4: Finished on: Wed Feb 12 14:54:18 CST 2014
pool-1-thread-7: Task task8: Finished on: Wed Feb 12 14:54:18 CST 2014
pool-1-thread-9: Task task9: Finished on: Wed Feb 12 14:54:20 CST 2014
注意:
1.当将Runnable对象放入Executor里执行时,当执行完毕后记得要关闭执行器,不然执行器的永久运行将导致程序不会退出(非守护线程不结束,程序是不会结束的)。
2.并不能因为执行器具有较好的线程管理能力而将大量的线程放入线程器里去执行,同样也会拖垮系统。所以在初始化时也可以指定执行器创建线程的容量,如
executor=(ThreadPoolExecutor)Executors.newFixedThreadPool(int number);
2.Callable
Callbale接口主要可以返回线程执行的结果,如当我们开辟多条线程去对数组进行排序时,假若每条线程执行的算法都不同,我们当然希望得到第一个排序完的时间(最优算法),或者我们需要知道每一个算法返回的时间,诸如此类的问题,都需要借助Callable,以下代码模拟了验证用户名和密码,随机返回结果,代码如下
public class Program{
public static void main(String[] agrs){
String username="fly";
String pwd="123456";
UserValidator v1=new UserValidator("v1");
UserValidator v2=new UserValidator("v2");
TaskValidator t1=new TaskValidator(v1,username,pwd);
TaskValidator t2=new TaskValidator(v2,username,pwd);
List<TaskValidator> taskList=new ArrayList<TaskValidator>();
taskList.add(t1);
taskList.add(t2);
ExecuteService es=(ExecuteService)Executors.newCachedThreadPool();
String result;
try {
result = executor.invokeAny(taskList);
System.out.printf("Main: Result: %s\n",result);
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
executor.shutdown();
}
}
class UserValidator{
private String name;
public UserValidator(String name){
this.name=name;
}
public boolean validate(String name,String pwd){
Random random=new Random();
try {
long duration=(long)(Math.random()*10);
System.out.printf("Validator %s: Validating a user during %d seconds\n",this.name,duration);
TimeUnit.SECONDS.sleep(duration);
} catch (InterruptedException e) {
return false;
}
return random.nextBoolean();
}
public String getName(){
return name;
}
}
class TaskValidator implements Callable<String>{
private UserValidator validator;
private String user;
private String pwd;
public TaskValidator(UserValidator validator,String user,String pwd ){
this.validator=validator;
this.user=user;
this.pwd=pwd;
}
@Override
public String call() throws Exception{
if (!validator.validate(user, password)) {
System.out.printf("%s: The user has not been found\n",validator.getName());
throw new Exception("Error validating user");
}
System.out.printf("%s: The user has been found\n",validator.getName());
return validator.getName();
}
}
Callable实现的步骤其实和直接调用Executor没太大区别,唯一不同的就是,需要将所有的任务放入List列表里,而List的元素类型必须为“? extends Callable”,然后调用类似上面代码的invokeAny(这里是返回第一个任务的结果,还有其他方法,如invokeAll可以返回所有结果为一个集合)。
3.ScheduledThreadPoolExecutor
ScheduledThreadPoolExecutor可以延迟执行任务,下面程序开启5个任务,分别是1,2,3,4,5秒后自动启动,代码如下:
public class Program{
public static void main(String[] agrs){
ScheduledThreadPoolExecutor executor=(ScheduledThreadPoolExecutor)Executors.newScheduledThreadPool(1);
System.out.printf("Main: Starting at: %s\n",new Date());
for (int i=0; i<5; i++) {
Task task=new Task("Task "+i);
executor.schedule(task,i+1 , TimeUnit.SECONDS);
}
executor.shutdown();
try {
executor.awaitTermination(10, TimeUnit.SECONDS);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.printf("Main: Ends at: %s\n",new Date());
}
}
class Task implements Callable<String>{
private String name;
public Task(String name){
this.name=name;
}
@Override
public String call() throws Exception{
System.out.printf("%s: Starting at : %s\n",name,new Date());
return "Hello, world";
}
}
ScheduledThreadPoolExecutor完成delay的效果主要靠,schedule(Callable<T> callable,long delay,TimeUnit t)这个函数完成的,第一个参数也可以是Runnable对象。而shutdown()只是通知虚拟机去关闭执行器,但是若发生任何异常则无法处理,所以需要利用awaitTerimination(long time,TimeUnit t)来等待所有任务完成。shutdown+awaitTermination算是一种优雅的关闭executor的方式了。