java并发编程学习(四)

1.Future、FutureTask

Future接口表示异步的结果计算,提供了检查计算是否完成、等待其完成以及获取计算结果的方法。提供了其他方法来确定任务是否正常完成或取消。

FutureTask是Future接口的实现类。

public class MyFuture {

	public static void main(String[] args) {
		FutureTask<String> futureTask = new FutureTask<>(new Callable<String>() {
			@Override
			public String call() throws Exception {
				Thread.sleep(1000);
				return "test";
			}
		});
		new Thread(futureTask).start();
		try {
			System.out.println(futureTask.get());
		} catch (InterruptedException | ExecutionException e) {
			e.printStackTrace();
		}
	}
}

以上程序的运行结果为:

调用get()方法可以获取FutureTask类中的callable的call()方法的执行结果。如果call()方法还没执行完,get()方法会等待call()方法执行完。也可以使用get(long timeout, TimeUnit unit)来在指定时间内获取结果,如果没有获取会抛出TimeoutException。

下面是get()方法的源码

    /**
     * @throws CancellationException {@inheritDoc}
     */
    public V get() throws InterruptedException, ExecutionException {
        int s = state;
        if (s <= COMPLETING)
            s = awaitDone(false, 0L);
        return report(s);
    }

可以看到get()方法中如果任务没有完成,会执行awaitDone()方法,在awaitDone()方法中,会在for循环中一直判断,如果没有完成就调用LockSupport.park(this)(参考之前的LockSupport的使用https://blog.csdn.net/zhangcjsyl/article/details/84546985

    /**
     * Awaits completion or aborts on interrupt or timeout.
     *
     * @param timed true if use timed waits
     * @param nanos time to wait, if timed
     * @return state upon completion
     */
    private int awaitDone(boolean timed, long nanos)
        throws InterruptedException {
        final long deadline = timed ? System.nanoTime() + nanos : 0L;
        WaitNode q = null;
        boolean queued = false;
        for (;;) {
            if (Thread.interrupted()) {
                removeWaiter(q);
                throw new InterruptedException();
            }

            int s = state;
            if (s > COMPLETING) {
                if (q != null)
                    q.thread = null;
                return s;
            }
            else if (s == COMPLETING) // cannot time out yet
                Thread.yield();
            else if (q == null)
                q = new WaitNode();
            else if (!queued)
                queued = UNSAFE.compareAndSwapObject(this, waitersOffset,
                                                     q.next = waiters, q);
            else if (timed) {
                nanos = deadline - System.nanoTime();
                if (nanos <= 0L) {
                    removeWaiter(q);
                    return state;
                }
                LockSupport.parkNanos(this, nanos);
            }
            else
                LockSupport.park(this);
        }
    }

而在FutureTask的run()方法中,会执行FutureTask类中的callable对象的call()方法,并将call()方法的返回值result作为参数执行set(result);

    public void run() {
        if (state != NEW ||
            !UNSAFE.compareAndSwapObject(this, runnerOffset,
                                         null, Thread.currentThread()))
            return;
        try {
            Callable<V> c = callable;
            if (c != null && state == NEW) {
                V result;
                boolean ran;
                try {
                    result = c.call();
                    ran = true;
                } catch (Throwable ex) {
                    result = null;
                    ran = false;
                    setException(ex);
                }
                if (ran)
                    set(result);
            }
        } finally {
            // runner must be non-null until state is settled to
            // prevent concurrent calls to run()
            runner = null;
            // state must be re-read after nulling runner to prevent
            // leaked interrupts
            int s = state;
            if (s >= INTERRUPTING)
                handlePossibleCancellationInterrupt(s);
        }
    }

在set()方法中将result赋给coutcome,最后执行finishCompletion();

    /**
     * Sets the result of this future to the given value unless
     * this future has already been set or has been cancelled.
     *
     * <p>This method is invoked internally by the {@link #run} method
     * upon successful completion of the computation.
     *
     * @param v the value
     */
    protected void set(V v) {
        if (UNSAFE.compareAndSwapInt(this, stateOffset, NEW, COMPLETING)) {
            outcome = v;
            UNSAFE.putOrderedInt(this, stateOffset, NORMAL); // final state
            finishCompletion();
        }
    }

finishCompletion()方法会执行LockSupport.unpark(t);这个t就是执行当前任务的线程类,然后执行了LockSupport.park(this)后处于等待的get()方法的线程就会被唤醒

    private void finishCompletion() {
        // assert state > COMPLETING;
        for (WaitNode q; (q = waiters) != null;) {
            if (UNSAFE.compareAndSwapObject(this, waitersOffset, q, null)) {
                for (;;) {
                    Thread t = q.thread;
                    if (t != null) {
                        q.thread = null;
                        LockSupport.unpark(t);
                    }
                    WaitNode next = q.next;
                    if (next == null)
                        break;
                    q.next = null; // unlink to help gc
                    q = next;
                }
                break;
            }
        }

        done();

        callable = null;        // to reduce footprint
    }

而get()方法的返回值,是report(s)的结果,在report()方法中,如果这个线程没有被取消,就返回outcome的值,而outcome的值正是在FutureTask的run()方法中执行完call()方法的结果result。

    /**
     * Returns result or throws exception for completed task.
     *
     * @param s completed state value
     */
    @SuppressWarnings("unchecked")
    private V report(int s) throws ExecutionException {
        Object x = outcome;
        if (s == NORMAL)
            return (V)x;
        if (s >= CANCELLED)
            throw new CancellationException();
        throw new ExecutionException((Throwable)x);
    }

由此可见,FutureTask、Callable也是依赖于Thread、Runnable而实现的,只不过在FutureTask的run()方法中会调用callable的call()方法,因此我们要执行任务的具体实现就可以直接重写call()方法了。

2.线程池

先来一段看似简单的代码:

public class MyExecutor {
	public static void main(String[] args) {
		ExecutorService es =  Executors.newFixedThreadPool(10);
		Future<String> future = es.submit(new Callable<String>(){
			@Override
			public String call() throws Exception {
				return "test ThreadPool";
			}
		});
		try {
			System.out.println(future.get());
		} catch (InterruptedException | ExecutionException e) {
			e.printStackTrace();
		}
	}
	
}

执行结果如下

以上代码的就是通过Executors类的newFixedThreadPool(int) 方法新建一个线程池es,然后用一个Future对象接收es.submit()的返回值,打印出future.get()。

下面来看源码了解线程池是怎么工作的。

首先看newFixedThreadPool(int) 这个方法:

    /**
     * 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>());
    }

这个方法返回了一个线程池对象,nThreads是线程池中线程的数量。再看这个ThreadPoolExecutor类的构造方法:

    /**
     * Creates a new {@code ThreadPoolExecutor} with the given initial
     * parameters and default thread factory and rejected execution handler.
     * It may be more convenient to use one of the {@link Executors} factory
     * methods instead of this general purpose constructor.
     *
     * @param corePoolSize the number of threads to keep in the pool, even
     *        if they are idle, unless {@code allowCoreThreadTimeOut} is set
     * @param maximumPoolSize the maximum number of threads to allow in the
     *        pool
     * @param keepAliveTime when the number of threads is greater than
     *        the core, this is the maximum time that excess idle threads
     *        will wait for new tasks before terminating.
     * @param unit the time unit for the {@code keepAliveTime} argument
     * @param workQueue the queue to use for holding tasks before they are
     *        executed.  This queue will hold only the {@code Runnable}
     *        tasks submitted by the {@code execute} method.
     * @throws IllegalArgumentException if one of the following holds:<br>
     *         {@code corePoolSize < 0}<br>
     *         {@code keepAliveTime < 0}<br>
     *         {@code maximumPoolSize <= 0}<br>
     *         {@code maximumPoolSize < corePoolSize}
     * @throws NullPointerException if {@code workQueue} is null
     */
    public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue) {
        this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
             Executors.defaultThreadFactory(), defaultHandler);
    }

在这个构造方法中会调用另一个构造方法:

    /**
     * Creates a new {@code ThreadPoolExecutor} with the given initial
     * parameters.
     *
     * @param corePoolSize the number of threads to keep in the pool, even
     *        if they are idle, unless {@code allowCoreThreadTimeOut} is set
     * @param maximumPoolSize the maximum number of threads to allow in the
     *        pool
     * @param keepAliveTime when the number of threads is greater than
     *        the core, this is the maximum time that excess idle threads
     *        will wait for new tasks before terminating.
     * @param unit the time unit for the {@code keepAliveTime} argument
     * @param workQueue the queue to use for holding tasks before they are
     *        executed.  This queue will hold only the {@code Runnable}
     *        tasks submitted by the {@code execute} method.
     * @param threadFactory the factory to use when the executor
     *        creates a new thread
     * @param handler the handler to use when execution is blocked
     *        because the thread bounds and queue capacities are reached
     * @throws IllegalArgumentException if one of the following holds:<br>
     *         {@code corePoolSize < 0}<br>
     *         {@code keepAliveTime < 0}<br>
     *         {@code maximumPoolSize <= 0}<br>
     *         {@code maximumPoolSize < corePoolSize}
     * @throws NullPointerException if {@code workQueue}
     *         or {@code threadFactory} or {@code handler} is null
     */
    public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory,
                              RejectedExecutionHandler handler) {
        if (corePoolSize < 0 ||
            maximumPoolSize <= 0 ||
            maximumPoolSize < corePoolSize ||
            keepAliveTime < 0)
            throw new IllegalArgumentException();
        if (workQueue == null || threadFactory == null || handler == null)
            throw new NullPointerException();
        this.acc = System.getSecurityManager() == null ?
                null :
                AccessController.getContext();
        this.corePoolSize = corePoolSize;
        this.maximumPoolSize = maximumPoolSize;
        this.workQueue = workQueue;
        this.keepAliveTime = unit.toNanos(keepAliveTime);
        this.threadFactory = threadFactory;
        this.handler = handler;
    }

下面是构造参数的说明:

corePoolSize 核心线程数,默认情况下核心线程会一直存活。
maximumPoolSize 线程池所能容纳的最大线程数。超过这个数的线程将被阻塞。
keepAliveTime 非核心线程的闲置超时时间,超过这个时间就会被回收。当设置allowCoreThreadTimeOut(true)时,线程池中核心线程空闲时间达到keepAliveTime也将关闭。
unit 时间单位
workQueue

阻塞任务队列

  • ArrayBlockingQueue:构造函数一定要传大小
  • LinkedBlockingQueue:构造函数不传大小会默认为65536(Integer.MAX_VALUE ),当大量请求任务时,容易造成 内存耗尽。
  • SynchronousQueue:同步队列,一个没有存储空间的阻塞队列 ,将任务同步交付给工作线程。
  • PriorityBlockingQueue : 优先队列
threadFactory 创建新线程的线程工厂
handler

当提交任务数超过maxmumPoolSize+workQueue之和时,任务会交给RejectedExecutionHandler来处理

  • AbortPolicy(默认):直接抛弃
  • CallerRunsPolicy:用调用者的线程执行任务
  • DiscardOldestPolicy:抛弃队列中最久的任务
  • DiscardPolicy:抛弃当前任务

然后我们再找submit()的具体实现,在ThreadPoolExecutor类中没有submit()方法,往上找到ThreadPoolExecutor类的父类AbstractExecutorService类中找到了submit()方法:

    /**
     * @throws RejectedExecutionException {@inheritDoc}
     * @throws NullPointerException       {@inheritDoc}
     */
    public <T> Future<T> submit(Callable<T> task) {
        if (task == null) throw new NullPointerException();
        RunnableFuture<T> ftask = newTaskFor(task);
        execute(ftask);
        return ftask;
    }

在这个方法中,将参数封装成一个RunableFuture对象,然后执行execute()方法,因为是在ThreadPoolExecutor类中调用的submit()方法,所以我们还是要在ThreadPoolExecutor找这个execute()方法:

    /**
     * Executes the given task sometime in the future.  The task
     * may execute in a new thread or in an existing pooled thread.
     *
     * If the task cannot be submitted for execution, either because this
     * executor has been shutdown or because its capacity has been reached,
     * the task is handled by the current {@code RejectedExecutionHandler}.
     *
     * @param command the task to execute
     * @throws RejectedExecutionException at discretion of
     *         {@code RejectedExecutionHandler}, if the task
     *         cannot be accepted for execution
     * @throws NullPointerException if {@code command} is null
     */
    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();
        if (workerCountOf(c) < corePoolSize) {
            if (addWorker(command, true))
                return;
            c = ctl.get();
        }
        if (isRunning(c) && workQueue.offer(command)) {
            int recheck = ctl.get();
            if (! isRunning(recheck) && remove(command))
                reject(command);
            else if (workerCountOf(recheck) == 0)
                addWorker(null, false);
        }
        else if (!addWorker(command, false))
            reject(command);
    }

这里看到了一个ctl的变量,是ThreadPoolExecutor类中的一个成员变量,在ThreadPoolExecutor类中可以看到下面一些成员属性和方法:

    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; }

这里我着重介绍一下这几个重要的成员变量:

COUNT_BITS:int位数减三,也就是29;

CAPACITY:1左移29位减1,用二进制表示就是0001 1111  1111 1111  1111 1111  1111 1111;

RUNNING :-1左移29位,用二进制表示就是1110 0000  0000 0000  0000 0000  0000 0000;

SHUTDOWN:0左移29位,还是0;

STOP:1左移29位,用二进制表示就是0010 0000  0000 0000  0000 0000  0000 0000;

TIDYING:2左移29位,用二进制表示就是0100 0000  0000 0000  0000 0000  0000 0000;

TERMINATED:3左移29位,用二进制表示就是0110 0000  0000 0000  0000 0000  0000 0000;

从注释和命名上可以知道,RUNNING 、SHUTDOWN、STOP、TIDYING、TERMINATED都是表示线程池运行状态,而ctl表示的是线程池当前的状态(状态和运行状态是我自己取的名字,也不知道怎么叫比较好),初始值是RUNNING 和0取逻辑或,也就是RUNNING 的值。

方法runStateOf(int c)返回c和~CAPACITY取逻辑与,我们可以知道~CAPACITY就是RUNNING ,RUNNING 前三位是1,那么返回结果中前三位就是c的二进制的前三位,RUNNING 后29位是0,那么返回结果中的后29位就是0。也就是说得到的结果是c的前三位加29个0,也就是c代表的运行状态值。

方法workerCountOf(int c)返回c和CAPACITY取逻辑与,CAPACITY前三位是0,得到的结果的前三位也就是0,CAPACITY后29位是1,得到的结果就是c的后29位,最终的结果也就是将c的前三位置0的值。也就是c这个状态下运行的线程数量。

总结一下,一个int数表示线程池现在状态,这个状态包括线程池的运行状态和线程池中线程的数量。这个int数的二进制前三位表示运行状态,后29位表示线程的数量。runStateOf()方法可以求出这个状态的运行状态,workerCountOf()方法可以求出这个状态的线程数量。(这一块看了好久才懂,果然是写jdk的大佬,打死我也写不出这样的代码。。。)

上面的五个运行状态值也可以表示运行状态,也就是当前线程池中的线程数量为0。

我们还是看到刚才的execute()方法,顾名思义execute()方法就是执行一个task,execute()方法的参数只要是Runnable就行,但如果是Futrue类型则可以异步地获得结果。ctl是一个AtomicInteger对象,get()方法就是取其对应的int值。如果当前线程池中的线程数量少于核心线程数量的话就执行addWorker()方法,这个方法是用来判断是否可以添加一个线程,方法的第二个参数core表示是否核心线程,true就是是否可以添加一个核心线程,false表示是否可以增加非核心线程,如果可以增加,就会将当前线程池中的线程数量加一。源码如下:

    /**
     * Checks if a new worker can be added with respect to current
     * pool state and the given bound (either core or maximum). If so,
     * the worker count is adjusted accordingly, and, if possible, a
     * new worker is created and started, running firstTask as its
     * first task. This method returns false if the pool is stopped or
     * eligible to shut down. It also returns false if the thread
     * factory fails to create a thread when asked.  If the thread
     * creation fails, either due to the thread factory returning
     * null, or due to an exception (typically OutOfMemoryError in
     * Thread.start()), we roll back cleanly.
     *
     * @param firstTask the task the new thread should run first (or
     * null if none). Workers are created with an initial first task
     * (in method execute()) to bypass queuing when there are fewer
     * than corePoolSize threads (in which case we always start one),
     * or when the queue is full (in which case we must bypass queue).
     * Initially idle threads are usually created via
     * prestartCoreThread or to replace other dying workers.
     *
     * @param core if true use corePoolSize as bound, else
     * maximumPoolSize. (A boolean indicator is used here rather than a
     * value to ensure reads of fresh values after checking other pool
     * state).
     * @return true if successful
     */
    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 (;;) {
                int wc = workerCountOf(c);
                if (wc >= CAPACITY ||
                    wc >= (core ? corePoolSize : maximumPoolSize))
                    return false;
                if (compareAndIncrementWorkerCount(c))
                    break retry;
                c = ctl.get();  // Re-read ctl
                if (runStateOf(c) != rs)
                    continue retry;
                // else CAS failed due to workerCount change; retry inner loop
            }
        }

        boolean workerStarted = false;
        boolean workerAdded = false;
        Worker w = null;
        try {
            w = new Worker(firstTask);
            final Thread t = w.thread;
            if (t != null) {
                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 rs = runStateOf(ctl.get());

                    if (rs < SHUTDOWN ||
                        (rs == SHUTDOWN && firstTask == null)) {
                        if (t.isAlive()) // precheck that t is startable
                            throw new IllegalThreadStateException();
                        workers.add(w);
                        int s = workers.size();
                        if (s > largestPoolSize)
                            largestPoolSize = s;
                        workerAdded = true;
                    }
                } finally {
                    mainLock.unlock();
                }
                if (workerAdded) {
                    t.start();
                    workerStarted = true;
                }
            }
        } finally {
            if (! workerStarted)
                addWorkerFailed(w);
        }
        return workerStarted;
    }

c是线程池的状态,rs是取的线程池的运行状态,如果rs大于等于SHUTDOWN并且不满足(rs == SHUTDOWN && firstTask == null &&  ! workQueue.isEmpty())的话就返回false,也就是,线程池的运行状态为RUNNING或者满足(rs == SHUTDOWN && firstTask == null &&  ! workQueue.isEmpty())会继续执行。继续执行会进入一个循环,如果当前线程池的线程数量大于CAPACITY(0001 1111  1111 1111  1111 1111  1111 1111,因为前三位是表示状态,后29位是数量,所以不能大于这个值)或者大于线程池的线程数量限制(如果core为true取核心线程池数量,如果core为false就取线程池的最大数量),就返回false。如果当前线程池没有达到数量限制,就会执行compareAndIncrementWorkerCount(c),这个方法如果返回true,就跳出循环,否则会一直循环。下面是compareAndIncrementWorkerCount()方法飞源码:

    /**
     * Attempts to CAS-increment the workerCount field of ctl.
     */
    private boolean compareAndIncrementWorkerCount(int expect) {
        return ctl.compareAndSet(expect, expect + 1);
    }

这个方法就是比较ctl与传入参数的大小,如果相等就给ctl+1并返回true,不相等就不操作并返回false,compareAndSet()方法是一个原子性操作。也就是说,如果别线程修改了ctl的值,在addWorker()方法中会进入下一次循环重新取ctl的值,确保ctl中表示的当前线程数量是准确的。

在成功将ctl的数量加一之后,先定义了workerStarted和workerAdded两个boolean变量,然后新建一个worker对象,Worker类中的thread属性是用其构造参数Runnable构造的一个Thread对象,接着在加锁条件下判断是否可以添加这个线程,可以的话在线程池的workers这个HashSet中添加这个Worker对象,将workerAdded置为true。然后启动线程t,并将workerStarted置为true,最后返回workerStarted。如果execute()的参数是Futrue类型,则会将结果保存到Future的outcome中,调用get()即可获得结果。

猜你喜欢

转载自blog.csdn.net/zhangcjsyl/article/details/85233358