Java进程 vs 线程、线程池

#Java进程 vs 线程、线程池

##1. 进程

Java进程中Runtime类封装了进程运行时的环境。每个 Java 应用程序都有一个 Runtime 类实例,使应用程序能够与其运行的环境相连接。一般不能实例化一个Runtime对象,应用程序也不能创建自己的 Runtime 类实例,但可以通过 getRuntime 方法获取当前Runtime运行时对象的引用。一旦得到了一个当前的Runtime对象的引用,就可以调用Runtime对象的方法去控制Java虚拟机的状态和行为。如:

Runtime

Java进程Process是一个抽象类,通过Runtime.getRuntime().exec(String)可以创建一个本地进程执行参数命令,返回Process的一个实例。如:

Process

process指向一个本地进程,相对于主进程来说,process指向的称为子进程。其中的getInputStream()是为了获取子进程的输出信息。明明是获取输出信息,为什么是InputStream呢?因为相对于主进程来说,子进程的输出就是main进程的输入,所以是InputStream。相反,如果要向子进程传递参数或者输入信息,则应该用OutputStream。但是不推荐用java 1.0引入的Process,而是用java 5.0的ProcessBuilder替代。
ProcessBuilder是java 5.0引入的,start()方法返回Process的一个实例,如,

ProcessBuilder

Runtime和ProcessBuilder创建进程的不同点,就是启动子进程时的命令形式不同。Runtime.getRuntime.exec()可以把命令和参数写在一个String中,用空格分开;ProcessBuilder则是构造函数的参数中,传递一个由命令和参数组成的list或数组。

Java使用子进程执行命令同时获取子进程命令的输出时,应该加上-l选项,如:

Process -l

##2. 线程概念

运行着的程序叫做进程,一个Java进程至少会有一个线程。创建Java线程的方式(一定要使用Thread,由于Thead实现了Runnable接口,也可以说一定会使用Runnable):

  1. new Thread();
  2. new Thread(new Runnable(){…});

Thread实现了Runnable接口,创建Thread实例时,如果不提供Runnable实例,需要实现Thread的run方法,否则就是一个空任务线程了。在同一个Thread上调用多次start方法会抛出InvalidThreadStateException异常。

##3. 线程异常

主线程中为什么不能使用try…catch捕获子线程抛出的异常?

这是由线程的本质特性决定的。那么应该怎么捕获?在子线程中写try … catch,或者实现线程类中的嵌套接口UncaughtExceptionHandler,实现其uncaughtException(Thread, Throwable)方法,然后给有可能抛出异常的线程注册这个异常处理器。Thread.setDefaultUncaughtExceptionHandler()可以给当前线程创建的每个子线程设置默认的异常处理器。如:

线程异常捕获

##4. 线程池异常捕获

令人困惑的是,线程池ThreadPoolExecutor中,只有通过execute提交的任务,才能将它抛出的异常交给UncaughtExceptionHandler。而通过submit提交的任务,无论是抛出的未受检异常还是受检异常,都将被认为是任务返回状态的一部分。如果一个由submit提交的任务由于抛出了异常而结束,那么这个异常将被Future.get()封装在ExecutionException中重新抛出(在通过Future.get()获取submit()结果时,需要捕获受检异常InterruptedException和ExecutionException)。如:

线程池异常

##5. 线程优先级

每个线程都有一个优先级,在Thread.MIN_PRIORITY和Thread.MAX_PRIORITY之间(分别为1和10)。默认情况下,新创建的线程都拥有和创建它的线程相同的优先级。main方法所关联的初始化线程拥有一个默认的优先级,这个优先级是Thread.NORM_PRIORITY (5)。线程的当前优先级可以通过getPriority方法获得,线程的优先级可以通过setPriority方法来动态的修改。线程优先级一般作用如:

  • 10:Crisis management(应急处理);
  • 7-9:Interactive, event-driven(交互相关,事件驱动);
  • 2-3:Background computation(后台计算);
  • 1:Run only if nothing else can(仅在没有任何线程运行时运行的)。

##6. 线程中断

中断只是把interrupt标志位设置为true,thread.interrupt()中断的有sleep()、wait()、join(),中断这些操作会抛出InterruptedException异常,但是interrupt标志位为false(这是因为把interrupt设置为了true,抛出InterruptedException后,该标志位又清空了)。Thread.interrupted()静态方法会清除当前线程的中断状态并返回前一个状态。(一个线程的中断状态是不允许被其他线程清除的)。中断发生的时刻只是在任务要进入到阻塞操作中和已经在阻塞操作中这两种情况。所以,有时候你虽然写了捕获中断异常的代码,但是却不一定能够通过catch来退出,因为如果该线程是在执行不可中断的I/O或者被阻塞的synchronized方法或者是其他的一些耗时的操作时,这时候你调用Thread.interrupt()来中断操作时,停止不了该线程的,只是会把interrupt标志位设置为true而已。如:

耗时操作无法被中断停止

##7. 线程池

Java创建线程池,应该使用ThreadPoolExecutor类。ThreadPoolExecutor是Java提供的线程池类,继承AbstractExecutorService并实现了其所有抽象方法。AbstractExecutorService是抽象类,实现了ExecutorService接口(ExecutorService提供了shutdown、awaitTermination、submit、execute、invokeAny、invokeAll等方法),ExecutorService继承了Executor接口(Executor定义了execute()方法)。构建ThreadPoolExecutor时,需要传入线程池核心大小、线程池最大大小、线程空闲存活时间、阻塞队列BlockingQueue、线程工厂ThreadFactory(可选)、拒绝处理器RejectExceptionHandler(可选)。如:

ThreadPool

当一个任务通过execute(Runnable)或者submit(Runnable或Callable)方法添加到线程池时,ThreadPoolExecutor工作流程:

  1. 如果此时线程池中的数量小于corePoolSize,即使线程池中的线程都处于空闲状态,也要创建新的线程来处理被添加的任务。
  2. 如果此时线程池中的数量等于corePoolSize,但是缓冲队列workQueue(阻塞队列)未满,那么任务被放入缓冲队列。
  3. 如果此时线程池中的数量等于corePoolSize,缓冲队列workQueue满,并且线程池中的数量小于maximumPoolSize,建新的线程来处理被添加的任务。
  4. 如果此时线程池中的数量大于corePoolSize,缓冲队列workQueue满,并且线程池中的数量等于maximumPoolSize,那么通过拒绝处理器handler所指定的策略来处理此任务。也就是:处理任务的优先级为:核心线程corePoolSize、任务队列workQueue、最大线程maximumPoolSize,如果三者都满了,使用handler处理被拒绝的任务。
  5. 当线程池中的线程数量大于corePoolSize时,如果某线程空闲时间超过keepAliveTime,线程将被终止。这样,线程池可以动态的调整池中的线程数。

所有 BlockingQueue 都可用于传输和保持提交的任务。可以使用此队列与线程池进行交互:

  1. 如果运行的线程少于 corePoolSize,则 Executor 始终首选添加新的线程,而不进行排队。
  2. 如果运行的线程等于或多于 corePoolSize,则 Executor 始终首选将请求加入队列,而不添加新的线程。
  3. 如果无法将请求加入队列,线程数未超过maximumPoolSize,则创建新的线程;线程数超过maximumPoolSize,则使用拒绝策略处理新任务。

Executors提供了好几种工厂方法来创建线程池,内部都是用了ThreadPoolExecutor,配合着不同的BlockingQueue和RejectExceptionHandler,如:

ExecutorService executorService1 = Executors.newSingleThreadExecutor();

public static ExecutorService newSingleThreadExecutor() {
    return new FinalizableDelegatedExecutorService
        (new ThreadPoolExecutor(1, 1,
                                0L, TimeUnit.MILLISECONDS,
                                new LinkedBlockingQueue<Runnable>()));
}
// LinkedBlockingQueue是无界的阻塞队列,不断有新任务提交时,可能抛出OutOfMemoryError。

ExecutorService executorService2 = Executors.newFixedThreadPool(10);

public static ExecutorService newFixedThreadPool(int nThreads) {
    return new ThreadPoolExecutor(nThreads, nThreads,
                                  0L, TimeUnit.MILLISECONDS,
                                  new LinkedBlockingQueue<Runnable>());
}
// LinkedBlockingQueue是无界的阻塞队列,不断有新任务提交时,可能抛出OutOfMemoryError。

ExecutorService executorService3 = Executors.newScheduledThreadPool(10);

public ScheduledThreadPoolExecutor(int corePoolSize) {
    super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
          new DelayedWorkQueue());
}
// maximumPoolSize被设置成了Integer.MAX_VALUE,不断有新任务提交时,阻塞队列又满了时,会不断创建新线程来执行任务,可能抛出OutOfMemoryError。

ExecutorService executorService4 = Executors.newCachedThreadPool();

public static ExecutorService newCachedThreadPool() {
    return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                  60L, TimeUnit.SECONDS,
                                  new SynchronousQueue<Runnable>());
}
// maximumPoolSize被设置成了Integer.MAX_VALUE,不断有新任务提交时,阻塞队列又满了时,会不断创建新线程来执行任务,可能抛出OutOfMemoryError。

注意:永远都不应该使用Executors的上面几个工厂方法来创建线程池,应该使用ThreadPoolExecutor来创建,这样线程池的每个参数更直观,尽可能的避免OutOfMemoryError。

线程池ThreadPoolExecutor有几个默认的拒绝处理器:

  1. ThreadPoolExecutor.AbortPolicy(),抛出RejectedExecutionException异常 。
  2. ThreadPoolExecutor.CallerRunsPolicy(),如果线程池未关闭,则在调用者中运行该任务。
  3. ThreadPoolExecutor.DiscardOldestPolicy(),抛弃旧的任务。
  4. ThreadPoolExecutor.DiscardPolicy(),抛弃当前的任务。

AbortPolicy的默认实现如下:

public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
    throw new RejectedExecutionException("Task " + r.toString() +
                                         " rejected from " +
                                         e.toString());
}

CallerRunsPolicy的默认实现如下:

public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
    if (!e.isShutdown()) {
        r.run();
    }
}

DiscardOldestPolicy的默认实现如下:

public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
   if (!e.isShutdown()) {
        e.getQueue().poll();
        e.execute(r);
    }
}

DiscardPolicy的默认实现如下:

public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
}

喜欢的可以关注微信公众号:

这里写图片描述


##参考

[1] http://gee.cs.oswego.edu/dl/cpj/mechanics.html “Java Concurrency Constructs”

发布了14 篇原创文章 · 获赞 37 · 访问量 11万+

猜你喜欢

转载自blog.csdn.net/c315838651/article/details/60871815
今日推荐