线程池
系统启动一个线程的成本是比较高的,涉及与操作系统的交互。
使用线程池可以很好的提高性能。线程池在系统启动时创建大量空闲线程,程序将Runnable或Callable对象传给线程池,线程池会启动一个线程执行他们的run()或call() 方法, 执行结束后,线程不会死亡, 而是回到线程池中进入空闲状态。
使用线程池还可以控制并发线程的数量。
Executors
Executors 工厂类来产生线程池:
public class Executors {
/**
* Creates a thread pool that reuses a fixed number of threads
* operating off a shared unbounded queue. At any point, at most
* {@code nThreads} threads will be active processing tasks.
* If additional tasks are submitted when all threads are active,
* they will wait in the queue until a thread is available.
* If any thread terminates due to a failure during execution
* prior to shutdown, a new one will take its place if needed to
* execute subsequent tasks. The threads in the pool will exist
* until it is explicitly {@link ExecutorService#shutdown shutdown}.
*
* @param nThreads the number of threads in the pool
* @return the newly created thread pool
* @throws IllegalArgumentException if {@code nThreads <= 0}
*/
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
/**
* Creates a thread pool that maintains enough threads to support
* the given parallelism level, and may use multiple queues to
* reduce contention. The parallelism level corresponds to the
* maximum number of threads actively engaged in, or available to
* engage in, task processing. The actual number of threads may
* grow and shrink dynamically. A work-stealing pool makes no
* guarantees about the order in which submitted tasks are
* executed.
*
* @param parallelism the targeted parallelism level
* @return the newly created thread pool
* @throws IllegalArgumentException if {@code parallelism <= 0}
* @since 1.8
*/
public static ExecutorService newWorkStealingPool(int parallelism) {
return new ForkJoinPool
(parallelism,
ForkJoinPool.defaultForkJoinWorkerThreadFactory,
null, true);
}
/**
* Creates a work-stealing thread pool using all
* {@link Runtime#availableProcessors available processors}
* as its target parallelism level.
* @return the newly created thread pool
* @see #newWorkStealingPool(int)
* @since 1.8
*/
public static ExecutorService newWorkStealingPool() {
return new ForkJoinPool
(Runtime.getRuntime().availableProcessors(),
ForkJoinPool.defaultForkJoinWorkerThreadFactory,
null, true);
}
/**
* Creates a thread pool that reuses a fixed number of threads
* operating off a shared unbounded queue, using the provided
* ThreadFactory to create new threads when needed. At any point,
* at most {@code nThreads} threads will be active processing
* tasks. If additional tasks are submitted when all threads are
* active, they will wait in the queue until a thread is
* available. If any thread terminates due to a failure during
* execution prior to shutdown, a new one will take its place if
* needed to execute subsequent tasks. The threads in the pool will
* exist until it is explicitly {@link ExecutorService#shutdown
* shutdown}.
*
* @param nThreads the number of threads in the pool
* @param threadFactory the factory to use when creating new threads
* @return the newly created thread pool
* @throws NullPointerException if threadFactory is null
* @throws IllegalArgumentException if {@code nThreads <= 0}
*/
public static ExecutorService newFixedThreadPool(int nThreads, ThreadFactory threadFactory) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>(),
threadFactory);
}
/**
* Creates an Executor that uses a single worker thread operating
* off an unbounded queue. (Note however that if this single
* thread terminates due to a failure during execution prior to
* shutdown, a new one will take its place if needed to execute
* subsequent tasks.) Tasks are guaranteed to execute
* sequentially, and no more than one task will be active at any
* given time. Unlike the otherwise equivalent
* {@code newFixedThreadPool(1)} the returned executor is
* guaranteed not to be reconfigurable to use additional threads.
*
* @return the newly created single-threaded Executor
*/
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
/**
* Creates an Executor that uses a single worker thread operating
* off an unbounded queue, and uses the provided ThreadFactory to
* create a new thread when needed. Unlike the otherwise
* equivalent {@code newFixedThreadPool(1, threadFactory)} the
* returned executor is guaranteed not to be reconfigurable to use
* additional threads.
*
* @param threadFactory the factory to use when creating new
* threads
*
* @return the newly created single-threaded Executor
* @throws NullPointerException if threadFactory is null
*/
public static ExecutorService newSingleThreadExecutor(ThreadFactory threadFactory) {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>(),
threadFactory));
}
/**
* Creates a thread pool that creates new threads as needed, but
* will reuse previously constructed threads when they are
* available. These pools will typically improve the performance
* of programs that execute many short-lived asynchronous tasks.
* Calls to {@code execute} will reuse previously constructed
* threads if available. If no existing thread is available, a new
* thread will be created and added to the pool. Threads that have
* not been used for sixty seconds are terminated and removed from
* the cache. Thus, a pool that remains idle for long enough will
* not consume any resources. Note that pools with similar
* properties but different details (for example, timeout parameters)
* may be created using {@link ThreadPoolExecutor} constructors.
*
* @return the newly created thread pool
*/
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
/**
* Creates a thread pool that creates new threads as needed, but
* will reuse previously constructed threads when they are
* available, and uses the provided
* ThreadFactory to create new threads when needed.
* @param threadFactory the factory to use when creating new threads
* @return the newly created thread pool
* @throws NullPointerException if threadFactory is null
*/
public static ExecutorService newCachedThreadPool(ThreadFactory threadFactory) {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>(),
threadFactory);
}
/**
* Creates a single-threaded executor that can schedule commands
* to run after a given delay, or to execute periodically.
* (Note however that if this single
* thread terminates due to a failure during execution prior to
* shutdown, a new one will take its place if needed to execute
* subsequent tasks.) Tasks are guaranteed to execute
* sequentially, and no more than one task will be active at any
* given time. Unlike the otherwise equivalent
* {@code newScheduledThreadPool(1)} the returned executor is
* guaranteed not to be reconfigurable to use additional threads.
* @return the newly created scheduled executor
*/
public static ScheduledExecutorService newSingleThreadScheduledExecutor() {
return new DelegatedScheduledExecutorService
(new ScheduledThreadPoolExecutor(1));
}
/**
* Creates a single-threaded executor that can schedule commands
* to run after a given delay, or to execute periodically. (Note
* however that if this single thread terminates due to a failure
* during execution prior to shutdown, a new one will take its
* place if needed to execute subsequent tasks.) Tasks are
* guaranteed to execute sequentially, and no more than one task
* will be active at any given time. Unlike the otherwise
* equivalent {@code newScheduledThreadPool(1, threadFactory)}
* the returned executor is guaranteed not to be reconfigurable to
* use additional threads.
* @param threadFactory the factory to use when creating new
* threads
* @return a newly created scheduled executor
* @throws NullPointerException if threadFactory is null
*/
public static ScheduledExecutorService newSingleThreadScheduledExecutor(ThreadFactory threadFactory) {
return new DelegatedScheduledExecutorService
(new ScheduledThreadPoolExecutor(1, threadFactory));
}
/**
* Creates a thread pool that can schedule commands to run after a
* given delay, or to execute periodically.
* @param corePoolSize the number of threads to keep in the pool,
* even if they are idle
* @return a newly created scheduled thread pool
* @throws IllegalArgumentException if {@code corePoolSize < 0}
*/
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
return new ScheduledThreadPoolExecutor(corePoolSize);
}
/**
* Creates a thread pool that can schedule commands to run after a
* given delay, or to execute periodically.
* @param corePoolSize the number of threads to keep in the pool,
* even if they are idle
* @param threadFactory the factory to use when the executor
* creates a new thread
* @return a newly created scheduled thread pool
* @throws IllegalArgumentException if {@code corePoolSize < 0}
* @throws NullPointerException if threadFactory is null
*/
public static ScheduledExecutorService newScheduledThreadPool(
int corePoolSize, ThreadFactory threadFactory) {
return new ScheduledThreadPoolExecutor(corePoolSize, threadFactory);
}
/**
* Returns an object that delegates all defined {@link
* ExecutorService} methods to the given executor, but not any
* other methods that might otherwise be accessible using
* casts. This provides a way to safely "freeze" configuration and
* disallow tuning of a given concrete implementation.
* @param executor the underlying implementation
* @return an {@code ExecutorService} instance
* @throws NullPointerException if executor null
*/
public static ExecutorService unconfigurableExecutorService(ExecutorService executor) {
if (executor == null)
throw new NullPointerException();
return new DelegatedExecutorService(executor);
}
/**
* Returns an object that delegates all defined {@link
* ScheduledExecutorService} methods to the given executor, but
* not any other methods that might otherwise be accessible using
* casts. This provides a way to safely "freeze" configuration and
* disallow tuning of a given concrete implementation.
* @param executor the underlying implementation
* @return a {@code ScheduledExecutorService} instance
* @throws NullPointerException if executor null
*/
public static ScheduledExecutorService unconfigurableScheduledExecutorService(ScheduledExecutorService executor) {
if (executor == null)
throw new NullPointerException();
return new DelegatedScheduledExecutorService(executor);
}
}
newCachedThreadPool(): 创建具有缓存功能的线程池, 系统根据需要创建线程, 这些线程被缓存在线程池中
newFixedThreadPool(): 可重用, 具有固定线程数的线程池
newSingleThreadExecutor(): 创建一个只有单线程的线程池。
newScheduledThreadPool(): 指定线程数, 指定延迟后执行
newSingleThreadScheduledExecutor(): 只有一个线程,在指定延迟后执行
newWorkStealingPool(): 可以设定并行级别,没有为CPU数,会创建足够的线程的线程池来支持并行级别。生成work stealing池,相当于后台线程池,如果所有的前台线程池都死了, work stealing池 中线程会自动死亡。
返回 ExecutorService 对象, 代表一个线程池,它可以执行Runnable对象或Callable对象所代表的线程。
Future<?> submit() : 将 Runnable或Callable对象提交线程池, 有空闲线程时执行任务, Future代表返回值。
ScheduledExecutorService对象代表延迟或周期性的执行任务的线程池:
ScheduledFuture<V> schedule() 可以指定延迟时间。
scheduleAtFixedRate() : 设置延迟时间, 延迟执行, 并且设定频率重复执行。
public class ThreadPoolTest {
public static void main(String[] args){
ExecutorService threadPool = Executors.newFixedThreadPool(6);
Runnable target = () -> {
for(int i = 0; i < 20; i++){
System.out.println(Thread.currentThread().getName() + " i : " + i);
}
};
threadPool.submit(target);
threadPool.submit(target);
//关闭线程池
threadPool.shutdown();
}
}
//Output
pool-1-thread-2 i : 0
pool-1-thread-2 i : 1
pool-1-thread-2 i : 2
pool-1-thread-2 i : 3
pool-1-thread-2 i : 4
pool-1-thread-1 i : 0
pool-1-thread-2 i : 5
pool-1-thread-1 i : 1
...
FolkJoinPool
public abstract class AbstractExecutorService implements ExecutorService {
}
@sun.misc.Contended
public class ForkJoinPool extends AbstractExecutorService {
}
ForkJoinPool 是 ExecutorService 的实现类, 支持将任务拆分成多个小任务, 再把多个小任务结果合并成总得计算结果。
ForkJoinPool 的构造器:
ForkJoinPool(int parallelism): 创建一个parallelism个并行线程的
ForkJoinPool():Runtime.getRuntime().availableProcessors()作为parallelism参数
commonPool(): 返回一个通用池, 运行状态不受 shutdown()或 shutdownNow()影响, 使用 System.exit();会自动终止。
创建实例后, 调用 submit(ForkJoinTask task) 或 invoke()来执行任务。
ForkJoinTask 是一个抽象类,它的两个子类:
RecursiveAction : 无返回值任务
RecursiveTask : 有返回值任务
class CalTask extends RecursiveTask<Integer>{
private static final int count = 20;
private int arr[];
private int start;
private int end;
public CalTask(int[] arr, int start, int end) {
this.arr = arr;
this.start = start;
this.end = end;
}
@Override
protected Integer compute() {
int sum = 0;
if(end - start < count){
for(int i = start; i < end; i++) {
sum += arr[i];
}
return sum;
}
else {
int middle = (start + end) /2 ;
System.out.println(middle);
CalTask left = new CalTask(arr, start, middle);
CalTask right = new CalTask(arr, middle, end);
left.fork();
right.fork();
return left.join() + right.join();
}
}
}
public class SumTest {
public static void main(String[] args) throws Exception{
int[] arr = new int[100];
Random random = new Random();
int total = 0;
for(int i = 0, len = arr.length; i < len; i++){
int tmp = random.nextInt(20);
total += (arr[i] = tmp);
}
System.out.println(total);
ForkJoinPool pool = ForkJoinPool.commonPool();
Future<Integer> future = pool.submit(new CalTask(arr, 0, arr.length));
System.out.println(future.get());
pool.shutdown();
}
}
//Output
973
50
25
12
37
75
62
87
973
ThreadLocal类
ThreadLocal 是 Thread Local Variable 线程局部变量,为每一个使用该变量的线程都提供一个变量值的副本 .
public class ThreadLocal<T> {
}
主要下面三个方法:
get() : 获取当前线程中副本中的值
remove(): 删除当前线程的值
set(): 设置当前线程中的值
class User{
private ThreadLocal<String> name = new ThreadLocal<>();
public User(String str) {
this.name.set(str);
System.out.println("==" + this.name.get());
}
public String getName() {
return name.get();
}
public void setName(String str) {
this.name.set(str);
}
}
class UserTest extends Thread{
private User user;
public UserTest(User user, String name) {
super(name);
this.user = user;
}
public void run(){
for(int i = 0; i < 10; i++){
if(i == 6){
user.setName(getName());
}
System.out.println(user.getName() + " i : " + i);
}
}
}
public class ThreadLocalTest {
public static void main(String[] args){
User user = new User("INIT");
new UserTest(user, "first").start();
new UserTest(user, "second").start();
}
}
//Output
==INIT
null i : 0
null i : 1
null i : 2
null i : 3
null i : 4
null i : 5
first i : 6
first i : 7
null i : 0
null i : 1
null i : 2
null i : 3
null i : 4
null i : 5
second i : 6
second i : 7
second i : 8
second i : 9
first i : 8
first i : 9