将线程管理委托给执行器

Java 9并发编程指南 目录

将线程管理委托给执行器

在Java 5之前,在使用Java并发API实现并发应用时,需要自己管理线程。首先实现Runnable接口或者继承Thread类,然后创建thread对象并使用其start()方法开始执行。我们还需要控制线程状态,来了解线程是否已经结束执行或者依然在运行。

Java版本5中,出现了作为执行线程池提供者的执行器概念。这种机制,通过Executor和ExecutorService接口以及ThreadPoolExecutor和ScheduledThreadPoolExecutor类实现,我们只专注于任务逻辑实现即可。通过实现任务并发送到执行器,执行器中的线程池用来创建、管理和结束线程。在Java版本7中,fork/join框架提供了另一种执行器机制的实现,专门处理可分解为更小的子问题的问题。这种方法有许多优点,如下所示:

  • 不需要创建所有任务的线程。当发送任务到执行器且被池中的线程执行时,我们节省了创建新线程的时间。如果应用需要执行很多任务,节省的总时间将非常显著且应用性能将更好。
  • 如果我们创建少量的线程,应用也将使用很少内存,同样性能会更好。
  • 通过实现Runnable或Callable接口来构建在执行器中执行的并发任务。Callable接口实现返回结果的任务,这远优于传统任务。
  • 当发送任务到执行器时,返回Future对象了解任务状态和返回结果,确保是否已经完成执行。
  • 通过ScheduledThreadPoolExecutor类实现的特殊执行器,我们能够调度任务,并重复执行它们。
  • 通过执行器能够轻松地控制资源。我们能够建立池中线程的最大数量,这样执行器每次运行的任务数量永远不会超过最大数量。

与直接使用线程相比,使用执行器有很多优势。本节将实现范例,展示如何使用执行器比单独创建线程得到更好的性能。

准备工作

本范例通过Eclipse开发工具实现。如果使用诸如NetBeans的开发工具,打开并创建一个新的Java项目。

实现过程

通过如下步骤实现范例:

  1. 创建名为Task的类,指定其实现Runnable接口:

    public class Task implements Runnable {
    
  2. 实现run()方法,创建重复1000000次的循环,每次进行一些整数的数学操作:

    	@Override
    	public void run() {
    		int r;
    		for (int i=0; i<1000000; i++) {
    			r=0;
    			r++;
    			r++;
    			r*=r;
    		}
    	}
    }
    
  3. 实现本范例主类,创建名为Main的类,包含main()方法:

    public class Main {
    	public static void main(String[] args) {
    
  4. 创建执行1000个任务的1000个线程,并等待执行结束,控制总执行时间:

    		Thread threads[]=new Thread[1000];
    		Date start,end;
    		start=new Date();
    		for (int i=0; i<threads.length; i++) {
    			Task task=new Task();
    			threads[i]=new Thread(task);
    			threads[i].start();
    		}
    		for (int i=0; i<threads.length; i++) {
    			try {
    				threads[i].join();
    			} catch (InterruptedException e) {
    				e.printStackTrace();
    			}
    		}
    		end=new Date();
    		System.out.printf("Main: Threads: %d\n", (end.getTime()-start.getTime()));
    
  5. 创建Executor对象,发送1000个任务,等待任务结束。计算总执行时间:

    		ThreadPoolExecutor executor=(ThreadPoolExecutor)Executors.newCachedThreadPool();
    		start=new Date();
    		for (int i=0; i<threads.length; i++) {
    			Task task=new Task();
    			executor.execute(task);
    		}
    		executor.shutdown();
    		try {
    			executor.awaitTermination(1, TimeUnit.DAYS);
    		} catch (InterruptedException e) {
    			e.printStackTrace();
    		}
    		end=new Date();
    		System.out.printf("Main: Executor: %d\n", (end.getTime()-start.getTime()));
    	}
    }
    

工作原理

范例整个执行过程中,使用执行器的执行时间始终比直接创建线程要短。如果应用中有大量任务的话,最好使用执行器。

更多关注

  • 本章“使用fork/join框架代替执行器”小节
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值