java--线程池及Callable接口的使用

一、线程池

  现有问题:

    线程是宝贵的内存资源,单个线程约占1MB的空间,过多分配易造成内存溢出

    频繁的创建及销毁线程会增加虚拟机回收频率、资源开销,造成程序性能下降

  定义:线程容器,可设定线程分配的数量上限

     将预先创建的线程对象存入池中,并重用线程池中的线程对象。

     避免频繁的创建和销毁

  线程池原理:将任务提交给线程池,由线程池分配线程、运行任务,并在当前任务结束后复用线程。

二、获取线程池

  常用线程池接口和类(所在包:java.util.concurrent)

    Executor:线程池的*接口

    ExecutorService:线程池接口,可通过submit(Runnable task)提交任务代码

    Executors工厂类:通过此类可以获得一个线程池。

package com.monv.chatper14_1;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * Executor : 线程池的根接口,execute()
 * ExecutorService : 包含管理线程池的一些方法,submit shutdown
 *    ThreadPoolExecutor
 *        SheduledThreadPoolExecutor
 * Executors:创建线程池的工具类
 *         (1)创建固定线程个数的线程池
 *         (2)创建缓存线程池,由任务的多少决定
 *         (3)创建单个线程池
 *         (4)创建调度线程池 调度:周期、定时执行
 * @author Monv
 *
 */
public class Demo1 {
    public static void main(String[] args) {
        //1.1创建固定线程的线程池
        //ExecutorService es = Executors.newFixedThreadPool(4);
        //1.2创建缓存线程池
        ExecutorService es = Executors.newCachedThreadPool();
        //1.3创建单个线程池
        //Executors.newSingleThreadExecutor();
        //1.4创建调度线程池
        //Executors.newScheduledThreadPool(2);
        //2.创建线程 (卖票的案例)
        Runnable ru = new Runnable() {
            private int ticket = 100;
            @Override
            public void run() {
                // TODO Auto-generated method stub
                while(true) {
                    if(ticket <=0) {
                        break;
                    }
                    System.out.println(Thread.currentThread().getName()+"卖了"+ticket+"张票");
                    ticket--;
                }
            }
        };
        //3.提交线程
        for(int i =0;i<5;i++) {
            es.submit(ru);
        }
        //4.关闭线程池
        es.shutdown();//如果任务正在执行,等所有任务执行完毕,再关闭线程池
//        es.shutdownNow();//不会等待,立即关闭
    }
    
}

三、Callable接口创建线程

  public interface Callable<V>{

         public V call() throws Exception;

        }

  Callable接口是JDK1.5加入的,与Runnable接口类似,实现之后代表一个线程任务。

  Callable具有泛型返回值、可以声明异常

  Callable和Runnable接口的区别

   1.Callable接口中的Call方法有返回值,Runnable接口中的Run方法没有返回值

   2.Callable接口中的Call方法有声明异常,Runnable接口中的Run方法没有异常

  注:Callable不能直接交给线程 需要把Callable转换为可执行的任务 用FutureTask接口来转换 这个接口继承了Runnable接口

  

  1.用Callable执行0-100 的和

package com.monv.chatper14_1;

import java.util.concurrent.Callable;
import java.util.concurrent.FutureTask;

/**
 * 演示Callable的使用
 * Callable 和 Runnable接口的区别
 * (1)Callable 接口中的Call方法有返回值,Runnable接口中的Run方法没有返回值
 * (2)Callable 接口中的Call方法有声明异常,Runnable接口中的Run方法没有异常
 * @author Monv
 *
 */
public class Demo2 {
    public static void main(String[] args) throws Exception{
        //功能需求:使用Callable计算1-100的和
        //1.创建Callable对象
        Callable<Integer> callable = new Callable<Integer>() {
            @Override
            public Integer call() throws Exception {
                System.out.println(Thread.currentThread().getName()+"开始计算");
                int sum = 0;
                for (int i =1;i<=100;i++) {
                    sum+=i;
                }
                return sum;
            }
        };
        //2.Callable不能直接交给线程 要把Callable对象转换为可执行的任务
        FutureTask<Integer> task= new FutureTask<>(callable);
        //3.创建线程
        Thread thread = new Thread(task);
        //4.启动线程
        thread.start();
        //5.获取结果
        Integer sum = task.get();
        
        System.out.println("结果是:"+sum);
    }
}

  

  2.使用线程池实现1-100的和

package com.monv.chatper14_1;

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

/**
 * 用线程池实现1-100的和
 * @author Monv
 *
 */
public class Demo3 {
    public static void main(String[] args) throws Exception {
        //1.创建线程池
        ExecutorService es = Executors.newFixedThreadPool(1);
        //2.提交任务 Future:表示将要执行完任务的结果
        Future<Integer> future = es.submit(new Callable<Integer>() {
            @Override
            public Integer call() throws Exception {
                System.out.println(Thread.currentThread().getName()+"开始计算");
                int sum = 0 ;
                for (int i =1;i<=100;i++) {
                    sum+=i;
                }
                return sum;
            }
        });
        //3.获取任务的结果
        System.out.println("结果为:"+future.get()); 
        //4.关闭线程池
        es.shutdown();
    }
}

 

  3.用Future 两个任务并发执行0-50 51-100 的和

package com.monv.chatper14_1;

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

/**
 * 用Future 两个任务并发执行0-50 51-100 的和
 * @author Monv
 *
 */
public class Demo4 {
    public static void main(String[] args) throws Exception{
        //1.创建线程池
        ExecutorService es= Executors.newFixedThreadPool(2);
        //2.提交任务
        Future<Integer> future1 = es.submit(new Callable<Integer>() {

            @Override
            public Integer call() throws Exception {
                int sum = 0;
                for(int i =1;i<=50;i++) {
                    sum+=i;
                }
                System.out.println("1-50计算完毕!");        
                return sum;
            }
        });
        Future<Integer> future2 = es.submit(new Callable<Integer>() {

            @Override
            public Integer call() throws Exception {
                int sum = 0;
                for(int i =51;i<=100;i++) {
                    sum+=i;
                }
                System.out.println("51-100计算完毕!");        
                return sum;
            }
        });
        //3.得到结果
        int sum = future1.get()+future2.get();
        System.out.println("1-100结果为:"+sum);
        //4.关闭线程池
        es.shutdown();
    }
}

  4.Future接口

    Future:表示将要完成任务的结果,可以通过Future来获得结果

        表示ExecutorService.submit()所返回的状态结果,就是call()返回值。

    方法:V get()以阻塞形式等待Future中的异步处理结果(call()的返回值)

  5.线程的同步及异步

    同步:形容一次方法调用,同步一旦开始,调用者必须等待该方法返回,才能继续   只要有等待 就是同步。一条执行路径

    异步:形容一次方法调用,异步一旦开始,像是一次消息传递,调用者告知之后立刻返回。二者竞争时间片,并发执行。多条执行路径

 

上一篇:创建线程的方式三:实现Callable接口


下一篇:多线程学习(下)