秋招准备-Java-并发编程-Executor框架(四)

1.带返回值的线程

2.Executor

3.线程池作用


1.带返回值的线程

    1.Callable接口

//java.util.concurrent.Callable
public interface Callable<V>
{
	V call() throws Exception;
}

        和Runnable接口相同,然后可以使用return语句,可以抛出异常这点的话,就是在call()方法里处理异常的时候可以选择当场捕获,或者抛出,然后到更上一层再处理吧,平时应该用得很少。  


    2.Future接口

//java.util.concurrent.Future
public interface Future<V>
{
    V get();
    V get(long timeout, TimeUnit unit);
    boolean cancel(boolean mayInterruptIfRunning);
    boolean isCancelled();
    boolean isDone();
}

        Future是用来进一步对线程操纵的接口,首先,要将Future对象与线程进行绑定,然后可以通过Future对象完成一些功能。

        Future对象的绑定是通过ExecutorService接口的submit()方法实现的,submit()方法传入一个Callable对象,返回一个Future对象,然后通过这个Future对象,可以获取Callable线程的返回值,完成情况等信息。

        get()方法用来获得计算结果,需要等待到线程进行到return那一步,因此还有一个带参数限定时间的get()方法

        cancel()用来取消任务的执行,已完成已取消等情况则直接返回false,而调用cancel()时,如果此任务还未开始,则取消任务(可以停掉synchronized的阻塞等待),如果已经开始执行,则看参数,如果为true意味着强制取消,false则让已开始任务执行完毕。

        isDone()可以用来判断任务的进行状况,调用cancel()后isDone()就是true。而isCancelled()则用来判断是否是取消的任务,要是通过cancel()成功取消的任务,才会返回true.

        <1>实验cancel()的取消功能

public class Main
{
	public static void main(String[] args)throws Exception
	{
		Object o = new Object();
		ExecutorService executor = Executors.newCachedThreadPool();
		executor.submit(new Runnable() {
			public void run() {
				synchronized(o) {
				try {
					Thread.sleep(3000);
					System.out.println("th1停3s");
				}catch(Exception ex) {}
				}
			}
		});
		Future<String> future = executor.submit(new Callable<String>(){
			public String call() {
				synchronized(o) {
					System.out.println("th2");
				}
				return "0";
			}
		});
		future.cancel(false);
	}
}

        结果是未打印“th2”,在synchronized的阻塞队列时,被cancel()取消任务。

		Future<?> future = executor.submit(new Runnable(){
			public void run() {
				synchronized(o) {
					System.out.println("th2");
				}
			}
		});
		future.cancel(false);

        将Callable对象改为Runnable对象后,发现会打印“th2”了,也就是停不掉Runnable对象。

        <2>实验get()方法

public class Main
{
	public static void main(String[] args)throws Exception
	{
		Object o = new Object();
		ExecutorService executor = Executors.newCachedThreadPool();
		executor.submit(new Runnable() {
			public void run() {
				synchronized(o) {
				try {
					Thread.sleep(5000);
					System.out.println("th1停5s");
				}catch(Exception ex) {}
				}
			}
		});
		Thread.sleep(500);
		Future<String> future = executor.submit(new Callable<String>(){
			public String call() {
				synchronized(o) {
					System.out.println("th2");
				}
				return "done";
			}
		});
//1		String s2 = future.get(3,TimeUnit.SECONDS);
//2		System.out.println("??");
		String s1 = future.get();
		System.out.println(s1);
//		System.out.println(s2);
	}
}

        取消注释1,2。当1语句在等待时,2语句不执行,等同于阻塞,1语句等待3秒后超时,不再等待并抛出异常,主线程gg。

        不取消注释1,2。get()一直等到Callable线程执行完,传出done。

        总结:虽然ExecutorService的submit()也可以传入Runnable对象,但更多还是与Callable搭配,完成传值,任务取消这两大特点。


    3.FutureTask

        FutureTask是实现类,它实现了RunnableFuture接口,而RunnableFuture接口又实现了Runnable与Future接口。

        因此,可以说FutureTask是Future的实现类(主接近Future的功能),拥有Future和Runnable的特性。

        <1>先说Runnable的特性,这个其实感觉很鸡肋,要用的话,那只能自定义类,然后继承FutureTask,再去重写run()方法,所以完全可以忽略了。并且用这种方式,完全联系不上Future的特性,就是单纯实现Runnable接口,中间还插了几层类关系,因此知道它的继承关系就行了。

        <2>再说Future的特性,可以从构造方法上体现出来。

        FutureTask<V>(Callable<V> callable);

        FutureTask<V>(Runnable runnable,V result);

        显然,就是把Future的绑定过程放到了FutureTask的构造器中,这样,只要将构造好的FutureTask对象拿去执行器执行,然后继续用自己本身的get(),cancel(),就能操纵线程了。

        <3>但是,用构造器,将Callable与Runnable对象封装到FutureTask里后,可以用Thread类来启动线程与得到返回值。如下:

public class Main
{
	public static void main(String[] args)throws Exception
	{
		FutureTask<String> ft = new FutureTask<String>(new Callable<String>() {
			public String call() {
				try {
					Thread.sleep(2000);
					System.out.println("call");
				}catch(Exception ex) {}
				return "Str";
			}
		});
		Thread t = new Thread(ft);
		t.start();
		System.out.println(ft.get());
	}
}



2.Executor

    结构图如下:


    Executor是顶级接口,然后执行器主要靠ExecutorService,真正搭建线程池在ThreadPoolExecutor,Executors有静态工厂方法来创建一些常用线程池。

    1.ExecutorService

public interface ExecutorService
{
	//sumbit()方法执行任务,返回Future对象
    Future<T> submit(Callable<T> task);
    Future<T> submit(Runnable task, T result);
    Future<?> submit(Runnable task);
    /*
     * shutdown()方法开始停止线程池
     * shutdown()执行完未执行完的任务,不接受新任务
     * shutdownNow()尝试着中断正在执行的线程
     */
    void shutdown();
    List<Runnable> shutdownNow();
}


    2.ThreadPoolExecutor

        说明一些基本的线程池参数:

public class ThreadPoolExecutor
{
    private volatile int corePoolSize;		//最大核心线程数
    private volatile int maximumPoolSize;	//最大线程总数
    private volatile long keepAliveTime;	//非核心线程闲置超时时长
    public ThreadPoolExecutor(
    	    int corePoolSize,
            int maximumPoolSize,
            long keepAliveTime,
            TimeUnit unit,
            BlockingQueue<Runnable> workQueue);
    /**
     * 传入新线程时:
     * 核心线程未满则创建核心线程
     * ->已满,总线程未满,则创建非核心线程
     * ->已满,阻塞队列未满,则线程进入阻塞队列等待
     * ->已满(阻塞队列有界),则抛出异常
     */
}


    3.Executors

public class Executors
{
	/**
	 * 线程数量固定(都为核心线程)
	 * 没有闲置超时
	 * 阻塞队列基于链表实现,无上界
	 */
    public static ExecutorService newFixedThreadPool(int nThreads) {
        return new ThreadPoolExecutor(nThreads, nThreads,
                                      0L, TimeUnit.MILLISECONDS,
                                      new LinkedBlockingQueue<Runnable>());
    }
    
    /**
     * 无核心线程,总线程数max
     * 有闲置超时,线程空闲60s即关闭
     * 阻塞队列只做个中转,传入后即执行
     */
    public static ExecutorService newCachedThreadPool() {
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                      60L, TimeUnit.SECONDS,
                                      new SynchronousQueue<Runnable>());
    }
    
    /**
     * 类似newFixedThreadPool(1)
     * 按顺序执行传入线程
     */
    public static ExecutorService newSingleThreadExecutor() {
        return new FinalizableDelegatedExecutorService
            (new ThreadPoolExecutor(1, 1,
                                    0L, TimeUnit.MILLISECONDS,
                                    new LinkedBlockingQueue<Runnable>()));
    }
    
    /**
     * 预定执行的线程池
     * 多了个schedule()方法,可以传入线程对象与延迟时间
     */
    public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
        return new ScheduledThreadPoolExecutor(corePoolSize);
    }
    public ScheduledThreadPoolExecutor(int corePoolSize) {
        super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
              new DelayedWorkQueue());
    }
}


3.线程池作用

    创建和销毁线程会带来系统开销,如果程序中有创建了大量生命期很短的线程,则频繁创建销毁线程会很影响效率,因此此时该考虑使用线程池。除此之外,如果在程序中使用过多的线程,则会占用很大的系统资源,这种情况也需要避免。因此线程池的作用可以简单归纳为:

    1.减少线程频繁创建和销毁的开销

    2.限制并发线程的数量

    3.对线程的执行进行管理,可以实现一些功能(顺序执行,延时执行)

    

    <1>使用单线程池实现线程T1,T2,T3按顺序执行

public class Main
{
	public static void main(String[] args)throws Exception
	{
		ExecutorService executor = Executors.newSingleThreadExecutor();
		Thread th1 = new Thread(){
			public void run() {
				System.out.println("T1");
			}
		};
		Thread th2 = new Thread(){
			public void run() {
				System.out.println("T2");
			}
		};
		Thread th3 = new Thread(){
			public void run() {
				System.out.println("T3");
			}
		};
		//execute()方法在Executor中定义,只能传入Runnable对象
		executor.execute(th1);
		executor.execute(th2);
		executor.execute(th3);
	}
}


    <2>……

    
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值