进程:系统分配资源的单位;
线程:处理器任务调度和执行的单位,线程之间共享进程资源。
学习大纲:
我这里把实现Callable接口和创建线程池调换了位置,因为在使用的过程中最常用的还是创建线程池的方法!但是目前所学知识有限,只用掌握创建线程的前两种方式即可(一、继承Thread类 二、实现Runnable接口)!!!
三、创建线程池:
线程池需要了解一个类和一个接口:Executors:工具类、线程池的工厂类;ExecutorService:真正的线程池接口,其子类为ThreadPoolExecutor。
池:即使池子(Pool),即我们可以在线程池中定义多个线程。
Executors:工具类、线程池的工厂类,用于创建并返回不同类型的线程池
- Executors.newCachedThreadPool():创建一个可根据需要创建新线程的线程池
- Executors.newFixedThreadPool(n):创建一个可重用固定线程数的线程池
- Executors.newSingleThreadExecutor() :创建一个只有一个线程的线程池
- Executors.newScheduledThreadPool(n):创建一个线程池,它可安排在给定延迟后运行命令或者定期地执行。
ExecutorService:真正的线程池接口,需要掌握三个方法。
方法详情:
execute(Runnable runnable):只能用来实现Runnable接口 sumbit(Callable callable):一般用来实现Callable接口,但是也可是实现Runnable接口 showdown():关闭连接池
想知道Callable是什么,往下学习!!!
public class DemoThreadPool02 { public static void main(String[] args) { //这里其实引用了多态的写法,ExecutorService是一个接口不能创建对象, //所以这里的service其实是ExecutorService接口的实现类对象ThreadPoolExecutor ExecutorService service = Executors.newFixedThreadPool(5); // System.out.println(service.getClass());//获取当前对象所属的类的路径class java.util.concurrent.ThreadPoolExecutor ThreadPoolExecutor poolExecutor = (ThreadPoolExecutor) service; FutureTask futureTask = new FutureTask(new PrintPrimeNumber()); service.submit(futureTask);//执行实现Callable接口的实现类对象的线程 service.execute(new PrintPrimeNumber02());//执行实现Runnable接口的实现类的线程 service.shutdown();//关闭连接池 } } /** * 打印100以内的质数 */ class PrintPrimeNumber02 implements Runnable{ @Override public void run() { for (int i = 2; i <= 100; i++) { boolean isFlag = true; for (int j = 2; j <= Math.sqrt(i); j++) { if (i % j == 0){ isFlag = false; break; } } if (isFlag == true) System.out.println(i); } } } /** * 打印100-200以内的质数 */ class PrintPrimeNumber implements Callable{ private int num = 100; @Override public Object call() throws Exception { for (int i = 100; i <= 200; i++) { boolean isFlag = true; for (int j = 2; j <= Math.sqrt(i); j++) { if (i % j == 0){ isFlag = false; break; } } if (isFlag == true) System.out.println(i); } return null; } }
四、实现Callable接口:
实现Callable接口有别于前两种创建线程的方式,实现Callable接口不是重写/实现run()而是实现call()方法,并且有返回值。
class Demo implements Callable { //有Object类型的返回值,如果分线程需要该线程提供值,然后继续线程时可以使用实现Callable接口 //如果不想要返回值,直接让返回值为null即可。 @Override public Object call() throws Exception { return null; } } class Demo implements Callable {
实现Callable接口需要结合Future接口使用,使用Future接口的唯一实现类FutureTask去接收返回值,注意此时并没有开启线程,如果开启线程的话还需要借助Thread类去,开始线程调用call()方法。
FutureTask实现类底层实现了RunnableFuture接口,而RunnableFuture接口底层继承自Runnable接口和Future接口...所以在使用new Thread类开启线程底层运用了和实现Runnable接口类似的效果。
案例:
public class DemoCallable02 { public static void main(String[] args) { ArraySums arraySums = new ArraySums(); FutureTask futureTask = new FutureTask(arraySums); //可是使用下面的方法一气呵成 // FutureTask futureTask = new FutureTask(new ArraySums()); Thread thread = new Thread(futureTask); thread.start(); try { Object sum = futureTask.get(); System.out.println("100以内的偶数和为:" + sum); } catch (InterruptedException e) { e.printStackTrace(); } catch (ExecutionException e) { e.printStackTrace(); } } } //计算100以内的偶数和 class ArraySums implements Callable { private int i = 1; private int sum = 0; @Override public Object call() throws Exception { while (true){ if (i <= 100){ if (i % 2 == 0){ System.out.println(i); sum += i; } i ++; }else { break; } } return sum; } }
案例说明:
- 如果我们不使用new Thread.start(),就不会执行call()方法中的内容,也就是还没有启动线程;
- 使用FutureTask实现类,目的是接收call()方法的返回值...FutureTask实现类中的get()方法会获取call()方法的返回值...另外FutureTask实现类中也定了很多对接call()的方法...
- 如果不用接收call()方法的返回值,可以将返回值设置为null,不用使用get()方法。
实现Callable接口的好处:
- Callable接口是有泛型的,可以规范用户的存储;
- Callable接口中的call()方法是有返回值的,可以方便线程间的通信;
- Callable接口中的call()方法是可以抛异常的,可以在后续异常进行捕获和处理