Java 线程池的使用【四】【Executors】

Java 线程池的使用【四】【Executors】,第1张

一、使用Executors创建线程池:

Executors类提供工厂方法用来创建不同类型的线程池:

ExecutorService executorService = Executors.具体类型的线程池;

通过调用返回值ExecutorService接口的基础方法execute或submit,将具体执行的线程任务传入到线程池中并执行。

线程任务的创建:跳转链接(下面例子都以Runnable接口为例)。

1)newCachedThreadPool

创建一个可根据需要创建新线程的线程池,但是在以前构造的线程可用时将重用它们。对于执行很多短期异步任务的程序而言,这些线程池通常可提高程序性能。

调用execute将重用以前构造的线程(如果线程可用)。如果现有线程没有可用的,则创建一个新线程并添加到池中。终止并从缓存中移除那些已有60s未被使用的线程。因此,长时间保持空闲的线程池不会使用任何资源。

ExecutorService cachedThreadPool = Executors.newCachedThreadPool();

cachedThreadPool.submit(new Runnable() {
    @Override
    public void run() {
        System.out.println("newCachedThreadPool");
    }
});

2)newFixedThreadPool

创建一个可重用固定线程数的线程池,以共享的无界队列方式来运行这些线程。在任意点,在大多数nThreads线程会处于处理任务的活跃状态。如果在所有线程处于活动状态时提交附加任务,则在有可用线程之前,附加任务将在队列中等待。如果在关闭前的执行期间由于失败而导致任何线程终止,那么一个新的线程将代替它执行后续的任务(如果需要)。在某个线程被显示地关闭之前,池中的线程将一直存在。

ExecutorService fixedThreadPool = Executors.newFixedThreadPool(2);

fixedThreadPool.submit(new Runnable() {
    @Override
    public void run() {
        System.out.println("newFixedThreadPool");
    }
});

3)newSingleThreadExecutor

创建一个只有一个线程的线程池,这个线程池可以在线程死亡后(或发生异常时)重新启动一个线程来替代原来的线程继续执行下去。

ExecutorService singleThreadPool = Executors.newSingleThreadExecutor();

singleThreadPool.submit(new Runnable() {
    @Override
    public void run() {
        System.out.println("newSingleThreadExecutor");
    }
});

4)newScheduledThreadPool

创建一个线程池,它可安排在给定延迟后运行命令或者定期地执行。

ScheduledExecutorService scheduledThreadPool= Executors.newScheduledThreadPool(3); 

scheduledThreadPool.submit(new Runnable() {
    @Override
    public void run() {
        System.out.println("");
    }
});

scheduledThreadPool.schedule(new Runnable() {
	@Override 
	public void run() {
		System.out.println("延迟三秒,只执行一次");
	}
}, 3, TimeUnit.SECONDS);

scheduledThreadPool.scheduleAtFixedRate(new Runnable() {
	@Override 
	public void run() {
		System.out.println("延迟一秒后,每三秒执行一次");
	}
}, 1, 3, TimeUnit.SECONDS);

scheduledThreadPool.scheduleWithFixedDelay(new Runnable() {
    @Override
    public void run() {
		System.out.println("延迟一秒后,等当前任务执行完毕后,推迟三秒后再执行一次");
    }
}, 1, 3, TimeUnit.SECONDS);
  • schedule方法是创建并执行在给定延迟后启用的一次性 *** 作,只执行一次,第一个参数可以传入Callable接口或Runnable接口。
  • scheduleAtFixedRate方法是固定的频率来执行某项计划,它不受每次计划执行时间的影响。到时间,它就执行;
  • scheduleWithFixedDelay方法,是相对任务。即无论当前任务执行多长时间,等执行完了,我再延迟指定的时间后再执行下一次,它受计划执行时间的影响。

5)newSingleThreadScheduledExecutor

创建一个单线程执行程序,它可安排在给定延迟后运行命令或者定期地执行。

注意:如果因为在关闭前的执行期间出现失败而终止了此单个线程,那么如果需要,和newSingleThreadExecutor一样,一个新线程会代替它执行后续的任务。

可保证顺序地执行各个任务,并且在任意给定的时间不会有多个线程是活动的。与其他等效的 newScheduledThreadPool(1) 不同,可保证无需重新配置此方法所返回的执行程序即可使用其他的线程。

和newScheduledThreadPool相同底层都是通过ScheduledThreadPoolExecutor实现的,所以也有对应的schedule、scheduleAtFixedRate和scheduleWithFixedDelay方法。

6)newWorkStealingPool

newWorkStealingPool是JDK1.8新增的线程池实现,通过原码可以看出上述线程池底层都是通过ScheduledThreadPoolExecutor或ThreadPoolExecutor类实现的,和其它线程池不同的是它底层是通过ForkJoinPool类实现的。

ThreadPoolExecutor和ForkJoinPool类一样都继承自AbstractExecutorService抽象类。

使用newWorkStealingPool的好处是:把1个任务拆分成多个“小任务”,把这些“小任务”分发到多个线程上执行。这些“小任务”都执行完成后,再将结果合并。

之前的线程池中,多个线程共有一个阻塞队列,而newWorkStealingPool 中每一个线程都有一个自己的队列。

当线程发现自己的队列没有任务了,就会到别的线程的队列里获取任务执行。可以简单理解为”窃取“。

一般是自己的本地队列采取LIFO(后进先出),窃取时采用FIFO(先进先出),一个从头开始执行,一个从尾部开始执行,由于偷取的动作十分快速,会大量降低这种冲突,也是一种优化方式。

它有两种具体的构造方法:

1、无参:

Runtime.getRuntime().availableProcessors()是获取当前系统可以的CPU核心数。

public static ExecutorService newWorkStealingPool() {
    return new ForkJoinPool
        (Runtime.getRuntime().availableProcessors(),
         ForkJoinPool.defaultForkJoinWorkerThreadFactory,
         null, true);
}

2、有参:

就一个参数parallelism,可以自定义并行度。

public static ExecutorService newWorkStealingPool(int parallelism) {
    return new ForkJoinPool
        (parallelism,
         ForkJoinPool.defaultForkJoinWorkerThreadFactory,
         null, true);
}

即:如果传入具体的参数则会创建相应的线程数,如果没有定义,则会创建系统CPU核心数对应的线程数。

三、停止线程池:

shutdown()方法调用后,线程池不再接收新任务,并且会将线程池中所有的任务执行后自动停止。

shutdownNow()方法调用后,线程池会强制中断所有线程立即停止。

四、线程池的ThreadFactory:

ThreadFactory简单来说就是用来创建线程的,其中也只是有一个newthread方法。

一些常用的作用:

  • 给线程命名,查看创建线程数
  • 给线程设置是否是后台运行
  • 设置线程优先级

1)创建ThreadFactory的实现(也可以直接使用匿名内部类的方式实现)

public class MyThreadFactory implements ThreadFactory{

    private final boolean isDaemon;
    
    MyThreadFactory(boolean isDaemon){
        this.isDaemon = isDaemon;
    }

    @Override
    public Thread newThread(Runnable r) {
        Thread newThread = new Thread(r);
        newThread.setDaemon(isDaemon);
        return newThread;
    }
}

2)将ThreadFactory传入线程池:

public static void main(String[] args) {
	ExecutorService threadPool = Executors.newFixedThreadPool(3, 
			new MyThreadFactory(true));
    threadPool.execute(new Runnable() {
        @Override
        public void run() {
            System.out.println("");
        }
    });
}

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/langs/793296.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-05-05
下一篇 2022-05-05

发表评论

登录后才能评论

评论列表(0条)

保存