java线程池相关总结

1.new Thread的弊端

new Thread(new Runnable() {
 
@Override
public void run() {
// TODO Auto-generated method stub
}
}).start();
  • a. 每次new Thread新建对象性能差。

  • b. 线程缺乏统一管理,可能无限制新建线程,相互之间竞争,及可能占用过多系统资源导致死机或oom。

  • c. 缺乏更多功能,如定时执行、定期执行、线程中断。

相比new Thread,Java提供的四种线程池的好处在于:

  • a. 重用存在的线程,减少对象创建、消亡的开销,性能佳。
  • b. 可有效控制最大并发线程数,提高系统资源的使用率,同时避免过多资源竞争,避免堵塞。
  • c. 提供定时执行、定期执行、单线程、并发数控制等功能。

2. java线程池

Java通过Executors提供四种线程池,分别为:

  • newCachedThreadPool创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。

  • newFixedThreadPool 创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。

  • newScheduledThreadPool 创建一个定长线程池,支持定时及周期性任务执行。

  • newSingleThreadExecutor 创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。

(1).newCachedThreadPool

创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。示例代码如下:

ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
for (int i = 0; i < 10; i++) {
final int index = i;
try {
Thread.sleep(index * 1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
 
cachedThreadPool.execute(new Runnable() {
 
@Override
public void run() {
System.out.println(index);
}
});
}

线程池为无限大,当执行第二个任务时第一个任务已经完成,会复用执行第一个任务的线程,而不用每次新建线程。上述情况情况只有1个线程被创建

(2). newFixedThreadPool

创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。示例代码如下:

ExecutorService fixedThreadPool = Executors.newFixedThreadPool(3);
for (int i = 0; i < 10; i++) {
final int index = i;
fixedThreadPool.execute(new Runnable() {
 
@Override
public void run() {
try {
System.out.println(index);
Thread.sleep(2000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
});
}

因为线程池大小为3,每个任务输出index后sleep 2秒,所以每两秒打印3个数字。线程数为3
这里如果使用Executors.newCachedThreadPool(),也会创建3个线程

(3) newScheduledThreadPool

定长线程池的大小最好根据系统资源进行设置。如Runtime.getRuntime().availableProcessors()。可参考PreloadDataCache

创建一个定长线程池,支持定时及周期性任务执行。延迟执行示例代码如下:

ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(5);
scheduledThreadPool.schedule(new Runnable() {
 
@Override
public void run() {
System.out.println("delay 3 seconds");
}
}, 3, TimeUnit.SECONDS);

表示延迟3秒执行。

定期执行示例代码如下:

scheduledThreadPool.scheduleAtFixedRate(new Runnable() {
 
@Override
public void run() {
System.out.println("delay 1 seconds, and excute every 3 seconds");
}
}, 1, 3, TimeUnit.SECONDS);

表示延迟1秒后每3秒执行一次

(4)、newSingleThreadExecutor 创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。示例代码如下:

ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor();
for (int i = 0; i < 10; i++) {
final int index = i;
singleThreadExecutor.execute(new Runnable() {
 
@Override
public void run() {
try {
System.out.println(index);
Thread.sleep(2000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
});
}

结果依次输出,相当于顺序执行各个任务。

3.Callable,Runnable和Future,FutureTask的区别及用法

Runnable和Callable的区别是:

  • (1)Callable规定的方法是call(),Runnable规定的方法是run()。其中Runnable可以提交给Thread来包装下,直接启动一个线程来执行,而Callable则一般都是提交给ExecuteService来执行。
  • (2)Callable的任务执行后可返回值,而Runnable的任务是不能返回值得
  • (3)call方法可以抛出异常,run方法不可以
  • (4)运行Callable任务可以拿到一个Future对象,c表示异步计算的结果。

Future 提供了检查计算是否完成的方法,以等待计算的完成,并获取计算的结果。计算完成后只能使用 get 方法来获取结果,如果线程没有执行完,Future.get()方法可能会阻塞当前线程的执行;如果线程出现异常,Future.get()会throws InterruptedException或者ExecutionException;如果线程已经取消,会抛出CancellationException。

取消由cancel(boolean mayInterruptRunning) 方法来执行。cancel(boolean mayInterruptRunning):如果任务还没开始,执行cancel(...)方法将返回false;如果任务已经启动,执行cancel(true)方法将以中断执行此任务线程的方式来试图停止任务,如果停止成功,返回true;当任务已经启动,执行cancel(false)方法将不会对正在执行的任务线程产生影响(让线程正常执行到完成),此时返回false;当任务已经完成,执行cancel(...)方法将返回false。mayInterruptRunning参数表示是否中断执行中的线程。

isDone()确定任务是正常完成还是被取消了。一旦计算完成,就不能再取消计算。如果为了可取消性而使用 Future 但又不提供可用的结果,则可以声明Future<?> 形式类型,并返回 null 作为底层任务的结果。

/**
 * 通过简单的测试程序来试验Runnable、Callable通过Executor来调度的时候与Future的关系
 */
package com.hadoop.thread;

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

public class RunnableAndCallable2Future {

    public static void main(String[] args) {

        // 创建一个执行任务的服务
        ExecutorService executor = Executors.newFixedThreadPool(3);
        try {
            //1.Runnable通过Future返回结果为空
            //创建一个Runnable,来调度,等待任务执行完毕,取得返回结果
            Future<?> runnable1 = executor.submit(new Runnable() {
                @Override
                public void run() {
                    System.out.println("runnable1 running.");
                }
            });
            System.out.println("Runnable1:" + runnable1.get());

            // 2.Callable通过Future能返回结果
            //提交并执行任务,任务启动时返回了一个 Future对象,
            // 如果想得到任务执行的结果或者是异常可对这个Future对象进行操作
            Future<String> future1 = executor.submit(new Callable<String>() {
                @Override
                public String call() throws Exception {
                    // TODO Auto-generated method stub
                    return "result=task1";
                }
            });
            // 获得任务的结果,如果调用get方法,当前线程会等待任务执行完毕后才往下执行
            System.out.println("task1: " + future1.get());

            //3. 对Callable调用cancel可以对对该任务进行中断
            //提交并执行任务,任务启动时返回了一个 Future对象,
            // 如果想得到任务执行的结果或者是异常可对这个Future对象进行操作
            Future<String> future2 = executor.submit(new Callable<String>() {
                @Override
                public String call() throws Exception {             
                    try {
                        while (true) {
                            System.out.println("task2 running.");
                            Thread.sleep(50);
                        }
                    } catch (InterruptedException e) {
                        System.out.println("Interrupted task2.");
                    }
                    return "task2=false";
                }
            });

            // 等待5秒后,再停止第二个任务。因为第二个任务进行的是无限循环
            Thread.sleep(10);
            System.out.println("task2 cancel: " + future2.cancel(true));

            // 4.用Callable时抛出异常则Future什么也取不到了
            // 获取第三个任务的输出,因为执行第三个任务会引起异常
            // 所以下面的语句将引起异常的抛出
            Future<String> future3 = executor.submit(new Callable<String>() {

                @Override
                public String call() throws Exception {
                    throw new Exception("task3 throw exception!");
                }

            });
            System.out.println("task3: " + future3.get());
        } catch (Exception e) {
            System.out.println(e.toString());
        }
        // 停止任务执行服务
        executor.shutdownNow();
    }
}

执行结果如下:

runnable1 running.
Runnable1:null
task1: result=task1
task2 running.
task2 cancel: true
Interrupted task2.
java.util.concurrent.ExecutionException: java.lang.Exception: Bad flag value!

FutureTask则是一个RunnableFuture,即实现了Runnbale又实现了Futrue这两个接口,另外它还可以包装Runnable和Callable,所以一般来讲是一个符合体了,它可以通过Thread包装来直接执行,也可以提交给ExecuteService来执行,并且还可以通过v get()返回执行结果,在线程体没有执行完成的时候,主线程一直阻塞等待,执行完则直接返回结果。

public class FutureTaskTest {

    /**
     * @param args
     */
    public static void main(String[] args) {
        Callable<String> task = new Callable<String>() {
            public String call() {
                System.out.println("Sleep start.");
                try {
                    Thread.sleep(1000 * 10);
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
                System.out.println("Sleep end.");
                return "time=" + System.currentTimeMillis();
            }
        };

        //直接使用Thread的方式执行
        FutureTask<String> ft = new FutureTask<String>(task);
        Thread t = new Thread(ft);
        t.start();
        try {
            System.out.println("waiting execute result");
            System.out.println("result = " + ft.get());
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (ExecutionException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

        //使用Executors来执行
        System.out.println("=========");
        FutureTask<String> ft2 = new FutureTask<String>(task);
        Executors.newSingleThreadExecutor().submit(ft2);
        try {
            System.out.println("waiting execute result");
            System.out.println("result = " + ft2.get());
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (ExecutionException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

    }
}

执行结果如下:

waiting execute result
Sleep start.
Sleep end.
result = time=1370844662537
=========
waiting execute result
Sleep start.
Sleep end.
result = time=1370844672542

FutureTask提交给ExecuteService示例:

//创建线程池
ExecutorService es = Executors.newSingleThreadExecutor();
//创建Callable对象任务
CallableDemo calTask=new CallableDemo();
//创建FutureTask
FutureTask<Integer> futureTask=new FutureTask<>(calTask);
//执行任务
es.submit(futureTask);

4.submit与execute的区别

(1)可以接受的任务类型

execute.jpg

submit.jpg

>-  execute只能接受Runnable类型的任务
>-  submit不管是Runnable还是Callable类型的任务都可以接受,但是Runnable返回值均为void,所以使用Future的get()获得的还是null

(2)返回值

  • execute没有返回值
  • submit有返回值,所以需要返回值的时候必须使用submit

(3)异常处理

  • execute

execute中的是Runnable接口的实现,所以只能使用try、catch来捕获CheckedException,通过实现UncaughtExceptionHandler接口处理UncheckedException,如果没有指定UncaughtExceptionHandler实现,由默认defaultUncaughtExceptionHandler处理

* Interface for handlers invoked when a <tt>Thread</tt> abruptly
* terminates due to an uncaught exception.
* <p>When a thread is about to terminate due to an uncaught exception
* the Java Virtual Machine will query the thread for its
* <tt>UncaughtExceptionHandler</tt> using
* {@link #getUncaughtExceptionHandler} and will invoke the handler's
* <tt>uncaughtException</tt> method, passing the thread and the
* exception as arguments.
* If a thread has not had its <tt>UncaughtExceptionHandler</tt>
* explicitly set, then its <tt>ThreadGroup</tt> object acts as its
* <tt>UncaughtExceptionHandler</tt>. If the <tt>ThreadGroup</tt> object
* has no
* special requirements for dealing with the exception, it can forward
* the invocation to the {@linkplain #getDefaultUncaughtExceptionHandler
* default uncaught exception handler}.
  • submit

submit不会主动抛出异常,不管提交的是Runnable还是Callable类型的任务,如果不对返回值Future调用get()方法,都会吃掉异常,调用get()方法在控制台打印出异常信息

checkedException:除了UncheckedException外的所有异常都是检查异常
UncheckedException:RuntimeException与其子类,以及错误(Error)

检查异常:
就是编译器要求你必须处置的异常。不知道你编程的时候有没有遇到过,你写的某段代码,编译器要求你必须要对这段代码try...catch,或者throws exception,如果你遇见过,没错,这就是检查异常,也就是说,你代码还没运行呢,编译器就会检查你的代码,会不会出现异常,要求你对可能出现的异常必须做出相应的处理。
对检查异常(checked exception)的几种处理方式:
1、继续抛出,消极的方法,一直可以抛到java虚拟机来处理,就是通过throws exception抛出。
2、用try...catch捕获

非检查异常:
编译器不要求强制处置的异常,虽然你有可能出现错误,但是我不会在编译的时候检查,没必要,也不可能。为什么呢?你想想非检查异常都有哪些?NullPointerException,IndexOutOfBoundsException,VirtualMachineError等,这些异常你编译的时候检查吗?那我还要不要运行了,等死人啊。再说了,明明可以运行时检查,都在编译的时候检查,你写的代码还能看吗?而且有些异常只能在运行时才能检查出来,比如空指针,堆溢出等。
对未检查的异常(unchecked exception )的几种处理方式:1、捕获 2、继续抛出 3、不处理
一般我们是不处理的,因为你很难判断会出什么问题,而且有些异常你也无法运行时处理,比如空指针,需要人手动的去查找。而且,捕捉异常并处理的代价远远大于直接抛出。

先来看下Callable定义:

public interface Callable<V> {
    V call() throws Exception;
}

其中的call能够抛出Exception异常,所以不管是CheckedException还是UncheckedException,直接抛出即可

public class ThreadExceptionTest {
	public static void main(String[] args) {
		ExecutorService executor = Executors.newCachedThreadPool();
		
		Future<Boolean> future = executor.submit(new CallableTask());
		try {
			future.get();   //这里不用get()方法就会吃掉异常
		} catch (InterruptedException e) {
			e.printStackTrace();
		} catch (ExecutionException e) {
			e.printStackTrace();
		}
		
		executor.shutdown();
	}
}
 
class CallableTask implements Callable<Boolean>{
	public Boolean call() throws Exception {
//		InputStream in = new FileInputStream(new File("xx.pdf"));
		int num = 3/0;
		return false;
	}
	
}
java.util.concurrent.ExecutionException: java.lang.ArithmeticException: / by zero
	at java.util.concurrent.FutureTask$Sync.innerGet(FutureTask.java:222)
	at java.util.concurrent.FutureTask.get(FutureTask.java:83)
	at my.ThreadExceptionTest.main(ThreadExceptionTest.java:21)
Caused by: java.lang.ArithmeticException: / by zero
	at my.CallableTask.call(ThreadExceptionTest.java:57)
	at my.CallableTask.call(ThreadExceptionTest.java:1)
	at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:303)
	at java.util.concurrent.FutureTask.run(FutureTask.java:138)
	at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:886)
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:908)
	at java.lang.Thread.run(Thread.java:619)

Runnable实现中自定义UncaughtExceptionHandler

public class ThreadExceptionTest {
	public static void main(String[] args) {
		ExecutorService executor = Executors.newCachedThreadPool();
		executor.execute(new RunnableTask());
		executor.shutdown();
	}

	static class RunnableTask implements Runnable {
		public void run() {
			Thread.currentThread().setUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
				@Override
				public void uncaughtException(Thread t, Throwable e) {
					System.out.println("One uncaught exception was got:");
					System.out.println("Thread id=" + t.getId());
					System.out.println("Thread name=" + t.getName());
					e.printStackTrace();
				}
			});
			int num = 3 / 0;
		}
	}
}
One uncaught exception was got:
java.lang.ArithmeticException: / by zero
Thread id=12
Thread name=pool-1-thread-1
	at interview.thread.ThreadExceptionTest$RunnableTask.run(ThreadExceptionTest.java:23)
	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
	at java.lang.Thread.run(Thread.java:748)
posted @ 2018-08-29 20:49  Anthony_ju  阅读(204)  评论(1编辑  收藏  举报