一、线程池的概念 点击此处返回总目录 二、使用线程池的第一种方式:使用Runnable接口的方式
三、使用线程池的第二种方式:使用Callable接口的方式 四、练习 一、线程池的概念 线程池就是一个容器,可以容纳多个线程。 创建和销毁线程要消耗系统资源。所以可以搞一个线程池,里面存进去线程,当运行任务的时候让线程运行,运行完了之后不能死,再放回到线程池中。 以前实现线程池都是程序员自己开发的,大致思路如下: 创建一个 ArrayList<Thread> 然后add很多个new Thread() 当使用的时候:Thread t = array.remove()取出一个线程来,然后执行任务 用完之后,ayyay.add(t),再把这个线程加回去。 从JDK5以后,内置了线程池技术,就可以直接使用了。 二、使用线程池的第一种方式:使用Runnable接口的方式 与两个类有关系,一个是工厂类,一个是线程池类。线程池容器是由线程池工厂创建的。 1. Executors类 //创建线程池的工厂类。在java.util.concurrent包下面。这个类下面的方法全是静态的。 常用方法: static ExecutorService newFixedThreadPool(int n) //创建一个可重用固定线程数的线程池。返回的是ExecutorService接口的实现 类的对象,即线程池对象。 2. ExecutorService接口 //这是一个接口,这个接口的实现类就是线程池类。 常用方法: Future submit(Runnable r) //参数是一个Runnable接口的对象。 返回的是Future接口的实现类。 线程池使用步骤: 1. 使用工厂类创建线程池对象,并指定线程的个数。返回ExecutorService接口的实现类对象(线程池对象)。 2. 线程池对象调用submit(Runnable r)方法,提交线程执行任务。 例: //SubThread.java
package cn.itcast.demo05; public class SubThread implements Runnable{ public void run(){ for(int i = 0;i<10;i++){ System.out.println(Thread.currentThread().getName()+"..."+i); } } } |
//SubThread1.java
package cn.itcast.demo05; public class SubThread1 implements Runnable { public void run(){ for(int i = 10; i<20; i++){ System.out.println(Thread.currentThread().getName()+"..."+i); } } } |
//Test.java
package cn.itcast.demo05; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; public class Test { public static void main(String[] args) { ExecutorService es = Executors.newFixedThreadPool(3); //工厂类创建线程池es SubThread task = new SubThread(); SubThread1 task1 = new SubThread1(); es.submit(task); es.submit(task); es.submit(task1); } } |
运行结果: pool-1-thread-2...0 pool-1-thread-3...10 pool-1-thread-1...0 pool-1-thread-3...11 pool-1-thread-3...12 pool-1-thread-3...13 pool-1-thread-2...1 pool-1-thread-3...14 pool-1-thread-1...1 pool-1-thread-3...15 ....... 三、使用线程池的第二种方式:使用Callable接口的方式 实现Runnable接口的方式看着挺好用,但是有两个问题: 一是,重写的是public void run()方法,没有返回值。二是run()方法没有抛出异常,所以接口的实现类也不能抛出异常。 Runnable接口已经定义好了,不能随便改。所以java又提供了另一个接口Callable。 Callable接口中有方法call(),等同于run(),但是有返回值,而且可以抛出异常。 V call() throws Exception 使用Callable接口的方式与Runnable接口的方式差不多。 例: //SubThread.java
package cn.itcast.demo06; import java.util.concurrent.Callable; public class SubThread implements Callable<String> { public String call(){ return "aaa"; } } |
//Test.java
package cn.itcast.demo06; import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; public class Test { public static void main(String[] args) throws InterruptedException, ExecutionException { ExecutorService es = Executors.newFixedThreadPool(2); Callable<String> c = new SubThread(); Future<String> f = es.submit(c); //submit的返回值是一个Future接口的实现类 String s = f.get(); //通过调用get()方法获得返回值 System.out.println(s); } } |
四、练习 使用线程池技术,有两个线程,一个线程计算1加到100的和,另一个线程计算1加到200的和。 分析: 1. 既然要返回结果,肯定不能使用Runnable的方式来做,要使用Callable的方式来做。 2. 一个任务是计算1到100的和,另一个任务是计算1到200的和,不能写两个子线程,而是应当把100和200作为参数传进去。call()方法不能接受参数,如果写了参数会报错。那怎么办呢?一个巧妙的方法就是,通过构造方法传进去。在实现类中定义一个变量,然后参数通过构造方法传进去。 //SubThread.java
package cn.itcast.demo07; import java.util.concurrent.Callable; public class SubThread implements Callable<Integer>{ public int a ; public SubThread(int a){ this.a = a; } public Integer call(){ int sum = 0 ; for(int i = 1;i <=a;i++){ sum +=i; } return sum; } } |
//Test.java
package cn.itcast.demo07; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; public class Test { public static void main(String[] args) throws Exception { ExecutorService es = Executors.newFixedThreadPool(2); Future<Integer> f = es.submit(new SubThread(100)); Future<Integer> f1 = es.submit(new SubThread(200)); System.out.println(f.get()); System.out.println(f1.get()); } } |
|