Java Executors

1、线程池的作用

1)重复利用已有的线程,避免线程的重复创建,从而节约系统资源。
2)通过设置线程池的corePoolSize,和maximumPoolSize,从而限制系统无限制的创建线程,节约宝贵的内存资源。
3)当已有的线程数超过corePoolSize时,通过设置keepAliveTime,来保证这些多出来的线程在空闲一段时间后能够被回收,而不是无限制的等待。采用这种机制,实现了系统的可伸缩性,线程数目不是固定的,可以根据系统的负载而动态变更。
4)在节约资源的同时,将系统的线程统一使用线程池进行管理,方便并且可靠。

2、Java中的线程池

Executors:框架中的工厂方法,通过以下的几个方法实现不同的线程池。
newFixedThreadPool,生成固定大小的线程池
newSingleThreadExecutor,生成单线程的线程池
newCachedThreadPool,无大小限制线程池。只要有任务需要执行,并且无空闲线程,则生成线程。线程空闲时间60s。
newSingleThreadScheduledExecutor,单线程定时任务框架
newScheduledThreadPool,多线程定时任务框架

AbstractExecutorService:线程池服务的抽象实现。
ThreadPoolExecutor:通用线程池的默认实现。Excutors中生成的前三种线程池都是ThreadPoolExcutor设置不同的参数得到的。
ScheduledThreadPoolExecutor:定时任务框架的实现。

3、ThreadPoolExecutor的参数

corePoolSize:线程池的工作线程数,即使它们空闲也会保存在线程池中,除非设置了allowCoreThreadTimeOut。
maximumPoolSize:线程池中允许的最大线程数。
keepAliveTime:当线程数超过工作线程数时,这个值表示这些多出的线程在被回收之前允许的最大空闲时间。
unit:keepAliveTime的单位。
workQueue:用于保存那些还未来得及执行的工作任务。只有在调用excute方法时,这些任务才可能被添加到workQueue中。
1)无界队列 LinkedBlockingQueue
当采用无界队列的时候,设置的maximumPoolSize会失效,永远不会用到到这个参数。当新的任务到来时,如果当前的工作线程数 < corePoolSize的时候,创建工作线程用于处理任务。否则将任务添加到无界队列中,如果添加失败则创建新的工作线程。但是由于无界队列的特殊性,不会出现添加失败的情况,因此任务总是能够成功添加到无界队列中。也就是说,一旦工作线程达到corePoolSize的时候,之后工作线程将会一直是这个数目,不会创建新的线程。由于无界队列不会拒绝新的任务,当产生任务的速度远高于任务执行的速度的时候(生产者 > 消费者),队列会迅速膨胀,导致资源的利用增加,同时后加入的任务迟迟无法得到执行,对于强调实时性的应用,这样是不可接受的。

例子:
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

public class ExecutorTest {
	public static void main(String[] args) {
		ExecutorService esv = new ThreadPoolExecutor(2, 10, 0,
				TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>(), new ThreadFactory() {
					private AtomicInteger count = new AtomicInteger(0);
					@Override
					public Thread newThread(Runnable r) {
						// TODO Auto-generated method stub
						return new Thread(r, "LinkedBlockingQueue " + count.incrementAndGet());
					}
				});
		for (int i = 0; i < 1000; i++) {
			esv.execute(new Runnable() {

				@Override
				public void run() {
					// TODO Auto-generated method stub
					System.out.println("abc");
				}
			});
			
			try {
				Thread.currentThread().sleep(500);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	}
}

采用jconsole查看程序的线程状况,

名字以LinkedBlockingQueue开头的线程有两个,正好符合线程池的corePoolSize。

2)有界队列 ArrayBlockingQueue等

当采用有界队列的时候,配合maximumPoolSize一起使用,可以有效的防止线程池的过度膨胀。当新的任务到来时,如果当前的工作线程数 <

 corePoolSize的时候,创建工作线程用于处理任务。否则将任务添加到队列中,如果添加任务失败则创建新的工作线程,用于处理新的任务,如果添

加成功,则使用已有的工作线程处理这个任务。所能创建的最大工作线程数是maxinumPoolSize,而超过corePoolSize的工作线程,会采用上面

keepAliveTime策略进行删除。因此在这种情况下,线程池的执行效率受有界队列的大小,maxinumPoolSize和corePoolSize的影响,当

maxinumPoolSize和corePoolSize设置的较大的时候,会导致线程数目增多,导致CPU使用率上升,资源利用率增高,以及频繁的上线文切换。但是

maxinumPoolSize和corePoolSize设置的较小的时候,会导致任务积压在队列中,无法得到及时的执行,导致系统吞吐量的降低。因此需要平衡线程池

大小(maxinumPoolSize和corePoolSize)和队列大小之间的关系,在保证吞吐量的同时,保证资源利用率。


例子:

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

public class ExecutorTest {
	public static void main(String[] args) {
		ExecutorService esv = new ThreadPoolExecutor(2, 10, 1,
				TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(2), new ThreadFactory() {
					private AtomicInteger count = new AtomicInteger(0);
					@Override
					public Thread newThread(Runnable r) {
						// TODO Auto-generated method stub
						return new Thread(r, "ArrayBlockingQueue " + count.incrementAndGet());
					}
				});
		for (int i = 0; i < 1000; i++) {
			esv.execute(new Runnable() {

				@Override
				public void run() {
					// TODO Auto-generated method stub
					try {
						Thread.currentThread().sleep(2000);
					} catch (InterruptedException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
					System.out.println("abc");
				}
			});
			
			try {
				Thread.currentThread().sleep(250);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	}
}

添加任务的速度是250ms,线程执行的时间是2000ms(2s),也就是差不多会同时存在2000ms / 250ms = 8个工作线程同时在工作。

使用jconsole查看程序的线程状况,


以ArrayBlockingQueue开头的线程有9个,和上面预估的8个线程是十分类似的。也就是说初始设置的核心线程数2,以及队列大小2,无法满足任务的执行,导致队列达到满负荷,然后导致了工作线程数的增加到了9。

当所有任务都执行完成后,查看线程状况,


工作线程数再次回到了初始的2,这是因为设置了keepAliveTime多出来的核心线程在空闲了一定时间后,会自动销毁。

那么扩大队列的大小,会出现什么情况呢?


可以看到即使任务产生的速率比较快,但是队列足够大,能够将任务缓存下来的话,线程池不会扩大核心线程数的数目。

继续观察,当缓存队列达到最大负荷的时候,会产生新的线程。


所以如何设置线程池大小和缓存队列大小会影响着线程池运行的性能。


3)直接提交队列 SynChronousQueue

当把一个任务加入到SynChronousQueue中时,只有当某个工作线程将这个任务取走,下一个任务才能成功添加到队列中,否则就会触发添加队列失

败。出现添加失败的时候,会生成新的核心线程。这个策略和有界队列是相同的。


例子:

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

public class ExecutorTest {
	public static void main(String[] args) {
		ExecutorService esv = new ThreadPoolExecutor(2, 10, 1,
				TimeUnit.SECONDS, new SynchronousQueue<Runnable>(), new ThreadFactory() {
					private AtomicInteger count = new AtomicInteger(0);
					@Override
					public Thread newThread(Runnable r) {
						// TODO Auto-generated method stub
						return new Thread(r, "SynchronousQueue " + count.incrementAndGet());
					}
				});
		for (int i = 0; i < 1000; i++) {
			esv.execute(new Runnable() {

				@Override
				public void run() {
					// TODO Auto-generated method stub
					try {
						Thread.currentThread().sleep(2000);
					} catch (InterruptedException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
					System.out.println("abc");
				}
			});
			
			try {
				Thread.currentThread().sleep(250);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	}
}

线程状态如下,


和有界队列相类似。


threadFactory:线程工厂,用于生成线程池里的线程。
handler:当执行被阻塞时,调用的句柄。也就是线程池满时的策略。

4、ThreadPoolExecutor的启动和接受任务

构造函数
<span style="font-size:14px;">    public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory,
                              RejectedExecutionHandler handler) {
	// 异常情况1
        if (corePoolSize < 0 ||
            maximumPoolSize <= 0 ||
            maximumPoolSize < corePoolSize ||
            keepAliveTime < 0)
            throw new IllegalArgumentException();
	// 异常情况2
        if (workQueue == null || threadFactory == null || handler == null)
            throw new NullPointerException();
	// 赋值
        this.corePoolSize = corePoolSize;
        this.maximumPoolSize = maximumPoolSize;
        this.workQueue = workQueue;
        this.keepAliveTime = unit.toNanos(keepAliveTime);
        this.threadFactory = threadFactory;
        this.handler = handler;
    }</span>
任务的执行,

<span style="font-size:14px;">	public void execute(Runnable command) {
        if (command == null)
            throw new NullPointerException();
        /*
         * Proceed in 3 steps:
         *
         * 1. If fewer than corePoolSize threads are running, try to
         * start a new thread with the given command as its first
         * task.  The call to addWorker atomically checks runState and
         * workerCount, and so prevents false alarms that would add
         * threads when it shouldn't, by returning false.
         *
         * 2. If a task can be successfully queued, then we still need
         * to double-check whether we should have added a thread
         * (because existing ones died since last checking) or that
         * the pool shut down since entry into this method. So we
         * recheck state and if necessary roll back the enqueuing if
         * stopped, or start a new thread if there are none.
         *
         * 3. If we cannot queue task, then we try to add a new
         * thread.  If it fails, we know we are shut down or saturated
         * and so reject the task.
         */
		 
		// 获取控制字段
        int c = ctl.get();
		// 当前核心线程数 < corePoolSize
        if (workerCountOf(c) < corePoolSize) {
			// 添加新的线程成功,并且将command作为这个线程首先执行的任务
            if (addWorker(command, true))
                return;
			// 重新获取控制字段
            c = ctl.get();
        }
		// 当前线程池仍然处于运行状态  并且   将任务command添加到等待队列成功
        if (isRunning(c) && workQueue.offer(command)) {
            int recheck = ctl.get();
			// 如果当前状态时非运行状态:SHUTDOWN或者STOP等  并且  从队列中删除任务成功
            if (! isRunning(recheck) && remove(command))
				// 拒绝任务
                reject(command);
			// 进入到这里的条件是:
			// 1)当前是运行状态
			// 2)当前非运行状态,同时删除任务失败
			// 当前已经没有worker了,创建新的worker,并且设置firstTask为null,因为这个任务已经添加到了队列中,然后调用getTask()获取这个任务。
            else if (workerCountOf(recheck) == 0)
                addWorker(null, false);
        }
		// 添加到队列中失败:可能的情况是采用有界队列或者直接提交队列。
        else if (!addWorker(command, false))
            reject(command);
    }</span>



在接续往下讲之前来看看线程池是如何表示状态和工作线程数目的,

<span style="font-size:14px;">    private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
    private static final int COUNT_BITS = Integer.SIZE - 3;
    private static final int CAPACITY   = (1 << COUNT_BITS) - 1;

    // runState is stored in the high-order bits
    private static final int RUNNING    = -1 << COUNT_BITS;
    private static final int SHUTDOWN   =  0 << COUNT_BITS;
    private static final int STOP       =  1 << COUNT_BITS;
    private static final int TIDYING    =  2 << COUNT_BITS;
    private static final int TERMINATED =  3 << COUNT_BITS;

    // Packing and unpacking ctl
    private static int runStateOf(int c)     { return c & ~CAPACITY; }
    private static int workerCountOf(int c)  { return c & CAPACITY; }
    private static int ctlOf(int rs, int wc) { return rs | wc; }</span>

ctl是控制字段,表示线程状态和工作线程数目。
COUNT_BITS = 29,表示使用ctl的低29位bit表示工作线程数目,使用ctl高3位bit表示线程状态。
CAPACITY = 2 ^ 29 - 1,表示最大的工作线程数。也就是0x1fffffff
RUNNING等表示线程状态,拿RUNNING为例子,RUNNING = (0xffffffff << 29 )= 0xe0000000

runStateOf(),从ctl控制字段中提取出线程池状态
workerCountOf(),从ctl控制字段中提取工作线程数目
ctlOf(),将线程池状态和工作线程组成ctl

例子:

初始状态下ctl的状态时RUNNING,工作线程数是0.

ctl = (RUNNING | 0) = (0xe0000000 | 0x00000000) = 0xe0000000
state = (0xe0000000 & ~0x1fffffff) = (0xe0000000 & 0xe0000000) = 0xe0000000 = RUNNING
workerCount = (0xe0000000 & 0x1fffffff) = 0x00000000 = 0

下面来看看addWorker是如何工作。


<span style="font-size:14px;">	private boolean addWorker(Runnable firstTask, boolean core) {
        retry:
        for (;;) {
            int c = ctl.get();
            int rs = runStateOf(c);

            // Check if queue empty only if necessary.
			// 当某些异常情况,直接终止线程的生成
            if (rs >= SHUTDOWN &&
                ! (rs == SHUTDOWN &&
                   firstTask == null &&
                   ! workQueue.isEmpty()))
                return false;

            for (;;) {
			    // 获取workerCount
                int wc = workerCountOf(c);
				// 当 1)workerCount >= CAPACITY
				//    2)如果core为true,则workerCount >= corePoolSize
				//	  3)如果core为false,则workerCount >= corePoolSize
				// 这三种情况下创建线程失败
                if (wc >= CAPACITY ||
                    wc >= (core ? corePoolSize : maximumPoolSize))
                    return false;
				// 利用cas原子操作使得c自增
                if (compareAndIncrementWorkerCount(c))
					// 如果自增成功,则直接中断内部循环和外部循环,回到标签retry处,然后不再执行retry,跳到
					// Worker w = new Worker(firstTask);继续执行
                    break retry;
				// 自增失败,从新获取c,因为其他线程可能修改了c
                c = ctl.get();  // Re-read ctl
				// 如果线程池状态改变了,那么直接中断内部和外部循环,回到标签retry处,然后继续从retry处往下执行。
                if (runStateOf(c) != rs)
                    continue retry;
                // else CAS failed due to workerCount change; retry inner loop
            }
        }
		
		// 修改ctl成功

		// 创建新的Worker,worker其实就是工作线程。firstTask为其第一个需要执行的任务。
        Worker w = new Worker(firstTask);
        Thread t = w.thread;

		// 获取锁
        final ReentrantLock mainLock = this.mainLock;
        mainLock.lock();
        try {
            // Recheck while holding lock.
            // Back out on ThreadFactory failure or if
            // shut down before lock acquired.
            int c = ctl.get();
            int rs = runStateOf(c);

            if (t == null ||
                (rs >= SHUTDOWN &&
                 ! (rs == SHUTDOWN &&
                    firstTask == null))) {
				// 异常情况下,workerCoutn--
                decrementWorkerCount();
                tryTerminate();
                return false;
            }

            workers.add(w);

            int s = workers.size();
            if (s > largestPoolSize)
                largestPoolSize = s;
        } finally {
            mainLock.unlock();
        }

		// 启动线程
        t.start();
        // It is possible (but unlikely) for a thread to have been
        // added to workers, but not yet started, during transition to
        // STOP, which could result in a rare missed interrupt,
        // because Thread.interrupt is not guaranteed to have any effect
        // on a non-yet-started Thread (see Thread#interrupt).
        if (runStateOf(ctl.get()) == STOP && ! t.isInterrupted())
            t.interrupt();

        return true;
    }</span>

总的来说就是检查一个新的工作线程是否能添加到workers中,如果能,创建新的线程添加。如果能,则开始启动线程,运行
firstTask。需要注意到的是在第一个大循环和后面的获取锁的时候都需要进行recheck。保证在多线程的时候,线程池被终止时,状态正常。

Worker是对线程的再次抽象,线程中的主要内容都在runWorker方法中,

<span style="font-size:14px;">	final void runWorker(Worker w) {
        Runnable task = w.firstTask; // 取得任务,如果是null,表示在生成worker的时候没有任务,则需要调用getTask()获取新的任务。
        w.firstTask = null;
        boolean completedAbruptly = true;
        try {
			// 工作线程主循环,对于在corePoolSize之内的工作线程,task永远不会是null,除非线程池终止。
			// 对于corePoolSize之外的工作线程,如果允许timeout,则在keepAliveTime之后,还没有新的任务供该工作线程使用,则task为null。则该线程进入
			// 终止模式
            while (task != null || (task = getTask()) != null) {
                w.lock();
                clearInterruptsForTaskRun();
                try {
                    beforeExecute(w.thread, task);
                    Throwable thrown = null;
                    try {
                        task.run();
                    } catch (RuntimeException x) {
                        thrown = x; throw x;
                    } catch (Error x) {
                        thrown = x; throw x;
                    } catch (Throwable x) 
                        thrown = x; throw new Error(x);
                    } finally {
                        afterExecute(task, thrown);
                    }
                } finally {
                    task = null;
                    w.completedTasks++;
                    w.unlock();
                }
            }
            completedAbruptly = false;
        } finally {
            processWorkerExit(w, completedAbruptly);
        }
    }</span>

那么线程池是如何检测超时的呢?

<span style="font-size:14px;">	private Runnable getTask() {
		// 还未超时
        boolean timedOut = false; // Did the last poll() time out?

        retry:
        for (;;) {
            int c = ctl.get();
            int rs = runStateOf(c);

            // Check if queue empty only if necessary.
			// 异常情况
            if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) {
                decrementWorkerCount();
                return null;
            }

			// 是否允许超时
            boolean timed;      // Are workers subject to culling?

            for (;;) {
                int wc = workerCountOf(c);
				// 如果allowCoreThreadTimeOut 或者 wc > corePoolSize
				// 前者表示所有的核心工作线程都允许timeout,这时不需要检查wc和corePoolSize的比较
				// 如果核心工作线程不允许timeout,则需要比较wc和corePoolSize的大小,只要wc超过corePoolSize,也就是有多余的工作线程时候,都允许超时。
				// 一旦wc降到corePoolSize的时候,超时机制被停止。
                timed = allowCoreThreadTimeOut || wc > corePoolSize;
				
				// 还未超时,则终止循环
                if (wc <= maximumPoolSize && ! (timedOut && timed))
                    break;
				// 已经超时的时候,如果c--成功,则返回null
                if (compareAndDecrementWorkerCount(c))
                    return null;
                c = ctl.get();  // Re-read ctl
				// c--失败,重新进行检查
                if (runStateOf(c) != rs)
                    continue retry;
                // else CAS failed due to workerCount change; retry inner loop
            }

            try {
				// 从workQueue取任务,如果timed也就是允许超时时,调用poll(keepAliveTime。。。),否则直接调用take。
                Runnable r = timed ?
                    workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
                    workQueue.take();
				// 如果取任务成功
                if (r != null)
                    return r;
				// 否则,超时
                timedOut = true;
            } catch (InterruptedException retry) {
				// 捕获InterruptedException,设置未超时
                timedOut = false;
            }
        }
    }</span>

5、ThreadPoolExecutor的终止

6、饱和策略

当有界队列被填充满后,饱和策略就会发挥作用。而这个饱和策略就是前面提到的参数handler。

在ThreadPoolExecutor中定义了几个不同的策略,分别是
CallerRunsPolicy,一种调节机制。既不会抛出异常,也不会简单的抛弃这个任务。而是将这个任务交给调用execute方法的那个线程去执行。保证了任务得到执行。
AbortPolicy,抛出异常RejectedExecutionException。
DiscardPolicy,直接放弃这个任务,不抛出异常
DiscardOldestPolicy,放弃最老任务的策略。

默认情况下采用的AbortPolicy,

例子:
<span style="font-size:14px;">import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

public class ExecutorTest {
	public static void main(String[] args) {
		ExecutorService esv = new ThreadPoolExecutor(2, 10, 1,
				TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(2), new ThreadFactory() {
					private AtomicInteger count = new AtomicInteger(0);
					@Override
					public Thread newThread(Runnable r) {
						// TODO Auto-generated method stub
						return new Thread(r, "ArrayBlockingQueue " + count.incrementAndGet());
					}
				});
		for (int i = 0; i < 25; i++) {
			final int j = i;
			esv.execute(new Runnable() {
				@Override
				public void run() {
					// TODO Auto-generated method stub
					try {
						Thread.currentThread().sleep(1000);
						System.out.println(j);
					} catch (InterruptedException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
				}
			});
		}
	}
}</span>


运行后打印出,
Exception in thread "main" java.util.concurrent.RejectedExecutionException: Task ExecutorTest$2@1326484 rejected from java.util.concurrent.ThreadPoolExecutor@16546ef[Running, pool size = 10, active threads = 10, queued tasks = 2, completed tasks = 0]
	at java.util.concurrent.ThreadPoolExecutor$AbortPolicy.rejectedExecution(Unknown Source)
	at java.util.concurrent.ThreadPoolExecutor.reject(Unknown Source)
	at java.util.concurrent.ThreadPoolExecutor.execute(Unknown Source)
	at ExecutorTest.main(ExecutorTest.java:21)
0
1
5
4
6
8
10
7
9
11
2
3

可以看到抛出了异常。

修改策略为CallerRunsPolicy,
esv.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());

打印信息如下,

0
8
6
4
10
7
5
9
1
11
12
3
2
13
15
14
16
21
17
18
20
19
23
22
24


修改策略为DiscardOldestPolicy,
esv.setRejectedExecutionHandler(new ThreadPoolExecutor.DiscardOldestPolicy());
1
11
9
7
5
0
8
6
4
10
23
24

修改策略为DiscardPolicy,
esv.setRejectedExecutionHandler(new ThreadPoolExecutor.DiscardPolicy());
1
5
7
9
0
11
4
6
8
10
3
2

上面两个的结果很类似,不同的是后者放弃了新加入的任务,前者放弃了先加入的任务。

7、关于线程池的大小

对于计算密集型的任务,在拥有N个处理器的系统上,当线程池的大小为N + 1时,通常能实现最优的利用率。然而对于那些包含I/O操作或者其他阻塞操作的任务,线程不会一直运行,因此需要的线程池更大。想要正确的设置线程池的大小,必须估算出任务的等待时间和计算时间的比值。这些数据可以通过某些分析工具得到。

设置:

N_cpu = CPU的个数
U_cpu = CPU的利用率
W/C = 等待时间和计算时间的比值

N_threads = N_cpu * U_cpu * (1 + W/C)。

在java中,N_cpu可以通过Runtime.getRuntime().availableProcessors()获得。

当然CPU周期并不是唯一影响线程池大小的资源,还包括内存、文件句柄、套接字句柄和数据库连接等。计算每个任务对于该资源的需求量,然后用该资源的可用总量除以每个任务的需求量,就可以得到线程数的上界。

8、一些细节

为什么threadFactory,handler,keepAliveTime,allowCoreThreadTimeOut,corePoolSize,maximumPoolSize使用volatile修饰?

这些数据的修改通过set函数,由于存在多线程修改的可能,因此可以通过volatile进行修饰,保证线程之间的可见性。




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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值