java基础

线程池

构造方法:

	public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory,
                              RejectedExecutionHandler handler)

ThreadFactory:线程工厂,可以指定线程池名字、线程名字,若不设置,则使用默认的线程工厂,且会给线程设置pool-1-thread-1这种名字,不利于线程堆栈信息的排查。另外还可以给线程设定UncaughtExceptionHandler,处理任务抛出的异常。

RejectedExecutionHandler:拒绝策略,

  • AbortPolicy ,丢弃任务并抛异常
  • DiscardPolicy ,丢弃任务
  • CallerRunsPolicy ,调用者(调用execute方法的线程)执行
  • DiscardOldestPolicy,丢弃最早入队的任务,尝试重新提交任务到线程池

任务提交到线程池的整体流程:

在这里插入图片描述
线程池的几种状态和数量:

	//利用一个控制变量ctl,把线程池状态和线程数量合在一起管理,
	//高3位表示状态,低29位表示线程数量
	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;//29个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; }

其中ctl这个AtomicInteger的功能很强大,其高3位用于维护线程池运行状态,低29位维护线程池中线程数量

1、RUNNING:-1<<COUNT_BITS,即高3位为1,低29位为0,该状态的线程池会接收新任务,也会处理在阻塞队列中等待处理的任务

2、SHUTDOWN:0<<COUNT_BITS,即高3位为0,低29位为0,该状态的线程池不会再接收新任务,但还会处理已经提交到阻塞队列中等待处理的任务

3、STOP:1<<COUNT_BITS,即高3位为001,低29位为0,该状态的线程池不会再接收新任务,不会处理在阻塞队列中等待的任务,而且还会中断正在运行的任务

4、TIDYING:2<<COUNT_BITS,即高3位为010,低29位为0,所有任务都被终止了,workerCount为0,为此状态时还将调用terminated()方法

5、TERMINATED:3<<COUNT_BITS,即高3位为011,低29位为0,terminated()方法调用完成后变成此状态

这些状态均由int型表示,大小关系为 RUNNING<SHUTDOWN<STOP<TIDYING<TERMINATED,这个顺序基本上也是遵循线程池从 运行 到 终止这个过程。

runStateOf(int c) 方法:c & 高3位为1,低29位为0的~CAPACITY,用于获取高3位保存的线程池状态

workerCountOf(int c)方法:c & 高3位为0,低29位为1的CAPACITY,用于获取低29位的线程数量

ctlOf(int rs, int wc)方法:参数rs表示runState,参数wc表示workerCount,即根据runState和workerCount打包合并成ctl

线程池中用到了较多的CAS,主要是利用ctl这个变量做worker数量的增减,以及线程池状态的变更。

Worker类:

/**
 * Class Worker mainly maintains interrupt control state for
 * threads running tasks, along with other minor bookkeeping.
 * This class opportunistically extends AbstractQueuedSynchronizer
 * to simplify acquiring and releasing a lock surrounding each
 * task execution.  This protects against interrupts that are
 * intended to wake up a worker thread waiting for a task from
 * instead interrupting a task being run.  We implement a simple
 * non-reentrant mutual exclusion lock rather than use
 * ReentrantLock because we do not want worker tasks to be able to
 * reacquire the lock when they invoke pool control methods like
 * setCorePoolSize.  Additionally, to suppress interrupts until
 * the thread actually starts running tasks, we initialize lock
 * state to a negative value, and clear it upon start (in
 * runWorker).
 * 
 * Worker类大体上管理着运行线程的中断状态 和 一些指标
 * Worker类投机取巧的继承了AbstractQueuedSynchronizer来简化在执行任务时的获取、释放锁
 * 这样防止了中断在运行中的任务,只会唤醒(中断)在等待从workQueue中获取任务的线程
 * 解释:
 *   为什么不直接执行execute(command)提交的command,而要在外面包一层Worker呢??
 *   主要是为了控制中断
 *   用什么控制??
 *   用AQS锁,当运行时上锁,就不能中断,TreadPoolExecutor的shutdown()方法中断前都要获取worker锁
 *   只有在等待从workQueue中获取任务getTask()时才能中断
 * worker实现了一个简单的不可重入的互斥锁,而不是用ReentrantLock可重入锁
 * 因为我们不想让在调用比如setCorePoolSize()这种线程池控制方法时可以再次获取锁(重入)
 * 解释:
 *   setCorePoolSize()时可能会interruptIdleWorkers(),在对一个线程interrupt时会要w.tryLock()
 *   如果可重入,就可能会在对线程池操作的方法中中断线程,类似方法还有:
 *   setMaximumPoolSize()
 *   setKeppAliveTime()
 *   allowCoreThreadTimeOut()
 *   shutdown()
 * 此外,为了让线程真正开始后才可以中断,初始化lock状态为负值(-1),在开始runWorker()时将state置为0,而state>=0才可以中断
 * 
 * 
 * Worker继承了AQS,实现了Runnable,说明其既是一个可运行的任务,也是一把锁(不可重入)
 */
private final class Worker
    extends AbstractQueuedSynchronizer
    implements Runnable
{
    /**
     * This class will never be serialized, but we provide a
     * serialVersionUID to suppress a javac warning.
     */
    private static final long serialVersionUID = 6138294804551838833L;
 
    /** Thread this worker is running in.  Null if factory fails. */
    final Thread thread; //利用ThreadFactory和 Worker这个Runnable创建的线程对象
     
    /** Initial task to run.  Possibly null. */
    Runnable firstTask;
     
    /** Per-thread task counter */
    volatile long completedTasks;
 
    /**
     * Creates with given first task and thread from ThreadFactory.
     * @param firstTask the first task (null if none)
     */
    Worker(Runnable firstTask) {
        //设置AQS的同步状态private volatile int state,是一个计数器,大于0代表锁已经被获取
        setState(-1); // inhibit interrupts until runWorker 
                      // 在调用runWorker()前,禁止interrupt中断,在interruptIfStarted()方法中会判断 getState()>=0
        this.firstTask = firstTask;
        this.thread = getThreadFactory().newThread(this); //根据当前worker创建一个线程对象
                                                          //当前worker本身就是一个runnable任务,也就是不会用参数的firstTask创建线程,而是调用当前worker.run()时调用firstTask.run()
    }
 
    /** Delegates main run loop to outer runWorker  */
    public void run() {
        runWorker(this); //runWorker()是ThreadPoolExecutor的方法
    }
 
    // Lock methods
    //
    // The value 0 represents the unlocked state. 0代表“没被锁定”状态
    // The value 1 represents the locked state. 1代表“锁定”状态
 
    protected boolean isHeldExclusively() {
        return getState() != 0;
    }
 
    /**
     * 尝试获取锁
     * 重写AQS的tryAcquire(),AQS本来就是让子类来实现的
     */
    protected boolean tryAcquire(int unused) {
        //尝试一次将state从0设置为1,即“锁定”状态,但由于每次都是state 0->1,而不是+1,那么说明不可重入
        //且state==-1时也不会获取到锁
        if (compareAndSetState(0, 1)) {
            setExclusiveOwnerThread(Thread.currentThread()); //设置exclusiveOwnerThread=当前线程
            return true;
        }
        return false;
    }
 
    /**
     * 尝试释放锁
     * 不是state-1,而是置为0
     */
    protected boolean tryRelease(int unused) {
        setExclusiveOwnerThread(null); 
        setState(0);
        return true;
    }
 
    public void lock()        { acquire(1); }
    public boolean tryLock()  { return tryAcquire(1); }
    public void unlock()      { release(1); }
    public boolean isLocked() { return isHeldExclusively(); }
 
    /**
     * 中断(如果运行)
     * shutdownNow时会循环对worker线程执行
     * 且不需要获取worker锁,即使在worker运行时也可以中断
     */
    void interruptIfStarted() {
        Thread t;
        //如果state>=0、t!=null、且t没有被中断
        //new Worker()时state==-1,说明不能中断
        if (getState() >= 0 && (t = thread) != null && !t.isInterrupted()) {
            try {
                t.interrupt();
            } catch (SecurityException ignore) {
            }
        }
    }
}

Worker实现的AQS为不可重入锁。
之所以Worker自己实现Runnable,并创建Thread,在firstTask外包一层,是因为要通过Worker控制中断,防止中断正在运行的线程:在任何任务执行之前,都需要对worker加锁;在shutdown()方法中断前都要获取worker的锁,由于此时已上锁,不能重入,所以就不能中断,只有在等待从workQueue中获取任务getTask()时才能中断

线程池底层存储结构就是个HashSet:
(为什么要用HashSet???)
在这里插入图片描述

关闭线程池:
shutdown:

Initiates an orderly shutdown in which previously submitted tasks are executed, but no new tasks will be accepted. Invocation has no additional effect if already shut down.
启动有序关闭,在此过程中执行以前提交的任务,但不接受任何新任务。如果已经关闭,调用将没有其他效果。

This method does not wait for previously submitted tasks to complete execution. Use {@link #awaitTermination awaitTermination} to do that.
此方法不等待以前提交的任务完成执行。使用{@link #awaitTermination awaitTermination}来完成。

	public void shutdown() {
        final ReentrantLock mainLock = this.mainLock;
        mainLock.lock();
        try {
            checkShutdownAccess();
            advanceRunState(SHUTDOWN);
            interruptIdleWorkers();
            onShutdown(); // hook for ScheduledThreadPoolExecutor
        } finally {
            mainLock.unlock();
        }
        tryTerminate();
    }

更改线程池状态为shutdown,并且中断阻塞在任务队列里取任务的空闲线程。

对于空闲线程,由于在getTask方法中,阻塞取take方法会抛出中断异常,捕获到异常后,继续自旋,检测到线程池状态为shutdown,且任务队列为空,getTask方法返回null,于是runWorker方法跳出while循环,执行结束,worker的run方法也执行结束,run方法结束了,线程也就结束退出了。

对于非空闲线程,在runworker方法中执行完任务后,会调用getTask方法到任务队列中取任务,此时检测到线程池状态为shutdown,任务队列非空,getTask方法不会返回null,会继续取出任务执行,直到任务队列空了,getTask方法才返回null,由此runWorker方法结束,worker的run方法也结束,线程也就结束。

shutdownNow:

Attempts to stop all actively executing tasks, halts the processing of waiting tasks, and returns a list of the tasks that were awaiting execution. These tasks are drained (removed) from the task queue upon return from this method.
尝试停止所有正在执行的任务,停止等待任务的处理,并返回等待执行的任务列表。从此方法返回时,将从任务队列中删除这些任务。

This method does not wait for actively executing tasks to terminate. Use {@link #awaitTermination awaitTermination} to do that.
此方法不会等待正在积极执行的任务终止。使用{@link #awaitTermination awaitTermination}来完成。

There are no guarantees beyond best-effort attempts to stop processing actively executing tasks. This implementation cancels tasks via {@link Thread#interrupt}, so any task that fails to respond to interrupts may never terminate.
除了尽最大努力停止处理正在执行的任务外,没有任何保证。此实现通过{@link Thread#interrupt}取消任务,因此任何未能响应中断的任务都可能永远不会终止。

	public List<Runnable> shutdownNow() {
        List<Runnable> tasks;
        final ReentrantLock mainLock = this.mainLock;
        mainLock.lock();
        try {
            checkShutdownAccess();
            advanceRunState(STOP);
            interruptWorkers();
            tasks = drainQueue();
        } finally {
            mainLock.unlock();
        }
        tryTerminate();
        return tasks;
    }

更改线程池状态为stop,中断所有线程(无论是否空闲),取出任务队列中的任务,并返回这些任务。

对于空闲线程,阻塞取take方法响应中断,抛出异常后,自旋,检测到线程池状态为stop,getTask方法直接返回null,runworker方法结束,worker的run方法结束,线程也就结束。

对于非空闲线程,由于正在runworker方法中执行任务,若任务能响应中断,则可能可以从任务的run方法中立即返回(比如一旦在方法中检测到中断,立即返回),返回后,调用getTask方法从任务队列中取任务,由于线程池状态为stop,方法直接返回null,runworker方法结束,worker的run方法结束,线程也就结束。

awaitTermination:

Blocks until all tasks have completed execution after a shutdown request, or the timeout occurs, or the current thread is interrupted, whichever happens first.
阻塞,直到shutdown方法调用后所有的任务都被执行完毕,或者超时发生,或者当前线程被中断,以先发生的情况为准。

	public boolean awaitTermination(long timeout, TimeUnit unit)
        throws InterruptedException {
        long nanos = unit.toNanos(timeout);
        final ReentrantLock mainLock = this.mainLock;
        mainLock.lock();
        try {
            for (;;) {
                if (runStateAtLeast(ctl.get(), TERMINATED))
                    return true;
                if (nanos <= 0)
                    return false;
                nanos = termination.awaitNanos(nanos);
            }
        } finally {
            mainLock.unlock();
        }
    }

可以看到知道线程池状态为terminated,方法才返回。方法通常用在shutdown方法之后,检测线程池是否已关闭。
shutdown方法中的tryTerminate方法会更改线程池状态为terminated,如下:

	final void tryTerminate() {
        for (;;) {
            int c = ctl.get();
            if (isRunning(c) ||
                runStateAtLeast(c, TIDYING) ||
                (runStateOf(c) == SHUTDOWN && ! workQueue.isEmpty()))
                return;
            if (workerCountOf(c) != 0) { // Eligible to terminate
                interruptIdleWorkers(ONLY_ONE);
                return;
            }

            final ReentrantLock mainLock = this.mainLock;
            mainLock.lock();
            try {
                if (ctl.compareAndSet(c, ctlOf(TIDYING, 0))) {
                    try {
                        terminated();
                    } finally {
                        ctl.set(ctlOf(TERMINATED, 0));
                        termination.signalAll();
                    }
                    return;
                }
            } finally {
                mainLock.unlock();
            }
            // else retry on failed CAS
        }
    }

参考:https://www.cnblogs.com/trust-freedom/p/6681948.html

任务抛出异常如何处理:

  • 通过ThreadFactory,给线程设置UncaughtExceptionHandler,在uncaughtException方法中处理异常
  • 使用ExecutorService.submit执行任务,利用返回的Future对象的get方法接收抛出的异常,然后进行处理
  • 重写ThreadPoolExecutor.afterExecute方法,处理传递到afterExecute方法中的异常
  • 在Runnable的run方法中捕获任务代码可能抛出的所有异常,包括未检测异常

UncaughtExceptionHandler:

Returns the handler invoked when this thread abruptly terminates due to an uncaught exception. If this thread has not had an uncaught exception handler explicitly set then this thread’s ThreadGroup object is returned, unless this thread has terminated, in which case null is returned.
返回该线程由于未捕获异常而突然终止时调用的处理程序。如果这个线程没有显式地设置未捕获异常处理程序,那么将返回这个线程的ThreadGroup对象,除非这个线程已经终止,在这种情况下将返回null。

public UncaughtExceptionHandler getUncaughtExceptionHandler() {
        return uncaughtExceptionHandler != null ?
            uncaughtExceptionHandler : group;
    }

Called by the Java Virtual Machine when a thread in this thread group stops because of an uncaught exception, and the thread does not have a specific {@link Thread.UncaughtExceptionHandler} installed.
当这个线程组中的一个线程由于未捕获异常而停止,并且该线程没有安装特定的{@link thread.UncaughtExceptionHandler}时,Java虚拟机将调用该线程。

The uncaughtException method of ThreadGroup does the following:
If this thread group has a parent thread group, the uncaughtException method of that parent is called with the same two arguments. Otherwise, this method checks to see if there is a {@linkplain Thread#getDefaultUncaughtExceptionHandler default uncaught exception handler} installed, and if so, its uncaughtException method is called with the same two arguments.Otherwise, this method determines if the Throwable argument is an instance of {@link ThreadDeath}. If so, nothing special is done. Otherwise, a message containing the thread’s name, as returned from the thread’s {@link Thread#getName getName} method, and a stack backtrace, using the Throwable’s {@link Throwable#printStackTrace printStackTrace} method, is printed to the {@linkplain System#err standard error stream}.
ThreadGroup的uncaughtException方法执行以下操作:如果这个线程组有一个父线程组,则使用相同的两个参数调用父线程组的uncaughtException方法。否则,该方法检查是否设置了默认的DefaultUncaughtExceptionHandler{@linkplain Thread#getDefaultUncaughtExceptionHandler default uncaught exception handler},如果是,则使用相同的两个参数调用它的uncaughtException方法。否则,此方法将确定Throwable参数是否是{@link ThreadDeath}的实例。如果是这样,就没有什么特别的事情要做。否则,从线程的{@link thread #getName getName}方法返回的包含线程名称的消息和使用Throwable的{@link Throwable#printStackTrace printStackTrace}方法返回的堆栈回溯将被打印到{@linkplain System#err标准错误流}。

Applications can override this method in subclasses of ThreadGroup to provide alternative handling of uncaught exceptions.
应用程序可以在ThreadGroup的子类中重写此方法,以提供对未捕获异常的替代处理。

public class ThreadGroup implements Thread.UncaughtExceptionHandler {
 public void uncaughtException(Thread t, Throwable e) {
        if (parent != null) {
            parent.uncaughtException(t, e);
        } else {
            Thread.UncaughtExceptionHandler ueh =
                Thread.getDefaultUncaughtExceptionHandler();
            if (ueh != null) {
                ueh.uncaughtException(t, e);
            } else if (!(e instanceof ThreadDeath)) {
                System.err.print("Exception in thread \""
                                 + t.getName() + "\" ");
                e.printStackTrace(System.err);
            }
        }
    }
}

FutureTask

/**
     * Possible state transitions:
     * NEW -> COMPLETING -> NORMAL 正常执行结束的流程
     * NEW -> COMPLETING -> EXCEPTIONAL 执行过程中出现异常的流程
     * NEW -> CANCELLED 被取消,即调用了cancel(false)
     * NEW -> INTERRUPTING -> INTERRUPTED 被中断,即调用了cancel(true)
     */
    private volatile int state;
    private static final int NEW          = 0;//init的时候
    private static final int COMPLETING   = 1;//表示已经执行完毕,但是结果变量却没有写outcome的状态。
    private static final int NORMAL       = 2;//成功
    private static final int EXCEPTIONAL  = 3;//返回异常
    private static final int CANCELLED    = 4;//取消
    private static final int INTERRUPTING = 5;//打断中
    private static final int INTERRUPTED  = 6;//打断结束
    /** The underlying callable; nulled out after running 
    底层的调用;运行后为空*/
    private Callable<V> callable;
    /** The result to return or exception to throw from get() */
    private Object outcome; // non-volatile, protected by state reads/writes
    /** The thread running the callable; CASed during run() */
    private volatile Thread runner;
    /** Treiber stack of waiting threads 
    FutureTask中用了Treiber Stack(一个基于CAS的无锁并发栈)来保存等待的线程*/
    private volatile WaitNode waiters;

Treiber Stack的实现套路很简单,就是CAS+重试:

@ThreadSafe
public class ConcurrentStack <E> {
    AtomicReference<Node<E>> top = new AtomicReference<Node<E>>();

    public void push(E item) {
        Node<E> newHead = new Node<E>(item);
        Node<E> oldHead;
        do {
            oldHead = top.get();
            newHead.next = oldHead;
        } while (!top.compareAndSet(oldHead, newHead));
    }

    public E pop() {
        Node<E> oldHead;
        Node<E> newHead;
        do {
            oldHead = top.get();
            if (oldHead == null)
                return null;
            newHead = oldHead.next;
        } while (!top.compareAndSet(oldHead, newHead));
        return oldHead.item;
    }

    private static class Node <E> {
        public final E item;
        public Node<E> next;

        public Node(E item) {
            this.item = item;
        }
    }
}

再看看futuretask怎么用这个无锁栈:

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

几种状态的变迁:
在这里插入图片描述

public void run() {
        if (state != NEW || //不是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)//等待cancel执行完,这里有点没想通,有意义吗?
                handlePossibleCancellationInterrupt(s);
        }
    }
    
	protected void set(V v) {
        if (UNSAFE.compareAndSwapInt(this, stateOffset, NEW, COMPLETING)) {
            outcome = v;
            UNSAFE.putOrderedInt(this, stateOffset, NORMAL); // final state
            finishCompletion();
        }
    }
    
    protected void setException(Throwable t) {
        if (UNSAFE.compareAndSwapInt(this, stateOffset, NEW, COMPLETING)) {
            outcome = t;
            UNSAFE.putOrderedInt(this, stateOffset, EXCEPTIONAL); // final state
            finishCompletion();
        }
    }
  • 没有异常:
    NEW->COMPLETING->NORMAL
  • 有异常:
    NEW->COMPLETING->EXCEPTIONAL
public boolean cancel(boolean mayInterruptIfRunning) {
        if (state != NEW)
            return false;
        if (mayInterruptIfRunning) {
            if (!UNSAFE.compareAndSwapInt(this, stateOffset, NEW, INTERRUPTING))
                return false;
            Thread t = runner;
            if (t != null)
                t.interrupt();
            UNSAFE.putOrderedInt(this, stateOffset, INTERRUPTED); // final state
        }
        else if (!UNSAFE.compareAndSwapInt(this, stateOffset, NEW, CANCELLED))
            return false;
        finishCompletion();
        return true;
    }
  • 如果任务状态非NEW,比如任务已经执行完成、或已经取消,则返回false,取消任务失败
  • 如果是cancel(false) 那么Task的状态变化就是
    NEW->=CANCELLED
  • 如果是cancel(true)那么Task的状态化就是
    NEW->INTERRUPTING ->INTERRUPTED

cancel方法通过调用interrupt,来打断等待任务,我们最好在代码里面判断interrupt,一旦出现interrupt,说明外部线程调用cancel了,我们需要快速返回。

get方法:

	public V get() throws InterruptedException, ExecutionException {
        int s = state;
        if (s <= COMPLETING)
            s = awaitDone(false, 0L);
        return report(s);
    }
    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);
    }

可以看出,

  • 若任务还没执行,调用cancel方法会更改任务状态,当任务执行时发现状态已经不是new了,就会放弃任务的执行

  • 若任务正在执行的过程中
    调用cancel(true),会把状态设置成INTERRUPTING,然后给线程设置中断标志位,再把状态设置成INTERRUPTED。如果callable.call()方法可以响应中断,可能对任务执行产生影响;如果方法不会响应中断,不会对任务运行产生影响,只会影响任务的状态。由于状态被更改为INTERRUPTED,所以调用get方法会抛出异常。

    调用cancel(false),状态从NEW变化为CANCELLED,不会对任务的执行造成影响,只会影响任务的状态。由于状态被更改为CANCELLED,所以调用get方法会抛出异常。

  • 若任务正在执行的过程中,或者已经执行完,但还没调用set(result)方法来设置结果并更改任务的状态,则此时调用cancel方法会更改任务的状态为cancelled或interrupted(根据cancel方法传入的参数为true或false决定,true意味着可能会通过中断线程,来取消任务,false则不中断),之后再调用get方法获取结果,会抛出异常。

例如以下代码:

public static void main(String[] args) throws ExecutionException, InterruptedException {
        ExecutorService executorService= new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                5L, TimeUnit.SECONDS,
                new SynchronousQueue<Runnable>());
        Future future=executorService.submit(new Runnable() {
            @Override
            public void run() {
                while(true);
            }
        });
        Thread.sleep(100);
        //future.cancel(false);
        future.cancel(true);
        future.get();
	}

回过头来再看下cancel方法的注释:

Attempts to cancel execution of this task. This attempt will fail if the task has already completed, has already been cancelled, or could not be cancelled for some other reason. If successful, and this task has not started when cancel is called, this task should never run. If the task has already started, then the mayInterruptIfRunning parameter determines whether the thread executing this task should be interrupted in an attempt to stop the task.
试图取消此任务的执行。如果任务已经完成,已经取消,或者由于其他原因无法取消,则此尝试将失败。如果成功,并且在调用cancel时此任务尚未启动,则此任务不应运行。如果任务已经启动,那么mayInterruptIfRunning参数确定执行此任务的线程是否应该中断,以试图停止该任务。

Lock

先看看api:

void lock();
void lockInterruptibly() throws InterruptedException;
boolean tryLock();
boolean tryLock(long time, TimeUnit unit) throws InterruptedException;
void unlock();
Condition newCondition();

lockInterruptibly:获取锁的过程中能够响应中断,也就是如果获取锁的过程中检测到中断,会抛出中断异常。这点与synchronized不同,synchronized忽略中断,继续获取锁。

tryLock:快速尝试获取锁,无所谓公平非公平,若要体现公平则调用tryLock(0,TimeUnit.SECONDS)

tryLock(long time, TimeUnit unit):可以体现公平性

unlock():在finally代码块中显示释放锁

newCondition:一个object对象只有一套监视器方法wait、notify、notifyAll,而一个lock,可以new多个condition对象,每个对象都有这些监视器方法,因此可以实现每个lock对象具有多个等待集的效果,更细粒度的进行线程间通信。lock可以代替synchronized,而condition可以代替对象监视器方法。

除此外,lock还有读写锁的实现,读读不互斥,synchronized不区分读写,一律互斥。

JDK与CGLIB动态代理

JDK:面向接口,限制性强,不灵活
CGLIB:面向类,生成的代理类是目标类的子类,底层通过ASM字节码生成框架实现。
性能对比:JDK生成代理速度比CGLIB快,但JDK1.6时,代理执行速度比CGLIB慢。JDK1.8时,代理执行速度已超过CGLIB。因此对于单例的代理或者可以池化管理的代理,因为不用频繁创建代理,所以可以选择CGLIB。

AOP生成的代理类可大致用以下代码理解:

class proxy {
    Object target;
    Object aspect;
    void do() {
        aspect.before();//前置通知
        try {
            target.do();
        }catch (Exception e) {
            aspect.afterThrowing();//异常通知
        }
        aspect.after();//后置通知
    }
}

Synchronized

对象的内存布局:
在这里插入图片描述
MarkWord:
在这里插入图片描述
age:GC分代年龄,4位的Java对象年龄。在GC中,对象每在Survivor区复制一次,年龄就增加1。当对象达到设定的阈值时,将会晋升到老年代。默认情况下,并行GC的年龄阈值为15,并发GC的年龄阈值为6。由于age只有4位,所以最大值为15,这就是-XX:MaxTenuringThreshold选项最大值为15的原因。

epoch:偏向锁的时间戳

锁分配膨胀过程:
在这里插入图片描述
JDK1.5-1.6进行的锁优化:
自适应自旋:自适应自旋的时间不固定,由前一次在同一个锁上的自旋时间及锁的拥有者状态来决定。如果在同一个锁对象上,自旋等待刚刚成功获得过锁,并且持有锁的线程正在运行中,那么虚拟机就会认为这次自旋也很有可能再次成功,进而它将允许自旋等待持续相对更长的时间,比如100个循环。另外,如果对于某个锁,自旋很少成功获得过,那在以后要获取这个锁时将可能省略掉自旋过程,以避免浪费处理器资源

锁消除:

public String concatString(String s1,String s2,String s3)
		StringBuffer sb=new StringBuffer();
		sb.append(s1);
		sb.append(s2);
		sb.append(s3);
		return sb.toString();
}

stringbuffer.append方法被加了synchronized修饰,3次append操作都要获取锁,但是stringbuffer的引用sb不会逃逸到其他线程栈里去,因此不会有其他线程访问到stringbuffer对象,所以对stringbuffer的操作是线程安全的,虚拟机会把锁消除掉。

锁粗化:还是对于以上代码,3次加锁,3次释放锁,可以优化成1次加锁,1次释放锁

	synchronized(sb) {
		sb.append(s1);
		sb.append(s2);
		sb.append(s3);
	}

轻量级锁:如锁分配膨胀过程图
偏向锁:如锁分配膨胀过程图

底层原理:

package com.paddx.test.concurrent;

public class SynchronizedDemo {
    public void method() {
        synchronized (this) {
            System.out.println("Method 1 start");
        }
    }
}

反编译结果:
在这里插入图片描述
monitorenter :

Each object is associated with a monitor. A monitor is locked if and only if it has an owner. The thread that executes monitorenter attempts to gain ownership of the monitor associated with objectref, as follows:
• If the entry count of the monitor associated with objectref is zero, the thread enters the monitor and sets its entry count to one. The thread is then the owner of the monitor.
• If the thread already owns the monitor associated with objectref, it reenters the monitor, incrementing its entry count.
• If another thread already owns the monitor associated with objectref, the thread blocks until the monitor’s entry count is zero, then tries again to gain ownership.

每个对象有一个监视器锁(monitor)。当monitor被占用时就会处于锁定状态,线程执行monitorenter指令时尝试获取monitor的所有权,过程如下:

1、如果monitor的进入数为0,则该线程进入monitor,然后将进入数设置为1,该线程即为monitor的所有者。

2、如果线程已经占有该monitor,只是重新进入,则进入monitor的进入数加1.

3.如果其他线程已经占用了monitor,则该线程进入阻塞状态,直到monitor的进入数为0,再重新尝试获取monitor的所有权。

ReentrantLock也是类似的原理,aqs中的state可以理解为monitor进入数
在这里插入图片描述

monitorexit:

The thread that executes monitorexit must be the owner of the monitor associated with the instance referenced by objectref.
The thread decrements the entry count of the monitor associated with objectref. If as a result the value of the entry count is zero, the thread exits the monitor and is no longer its owner. Other threads that are blocking to enter the monitor are allowed to attempt to do so.

执行monitorexit的线程必须是objectref所对应的monitor的所有者。

指令执行时,monitor的进入数减1,如果减1后进入数为0,那线程退出monitor,不再是这个monitor的所有者。其他被这个monitor阻塞的线程可以尝试去获取这个 monitor 的所有权。

ReentrantLock也是类似的原理:
在这里插入图片描述

通过这两段描述,我们应该能很清楚的看出Synchronized的实现原理,Synchronized的语义底层是通过一个monitor的对象来完成,其实wait/notify等方法也依赖于monitor对象,这就是为什么只有在同步的块或者方法中才能调用wait/notify等方法,否则会抛出java.lang.IllegalMonitorStateException的异常的原因。

我们再来看一下同步方法的反编译结果:

源代码:

package com.paddx.test.concurrent;

public class SynchronizedMethod {
    public synchronized void method() {
        System.out.println("Hello World!");
    }
}

在这里插入图片描述
从反编译的结果来看,方法的同步并没有通过指令monitorenter和monitorexit来完成(理论上其实也可以通过这两条指令来实现),不过相对于普通方法,其常量池中多了ACC_SYNCHRONIZED标示符。JVM就是根据该标示符来实现方法的同步的:当方法调用时,调用指令将会检查方法的 ACC_SYNCHRONIZED 访问标志是否被设置,如果设置了,执行线程将先获取monitor,获取成功之后才能执行方法体,方法执行完后再释放monitor。在方法执行期间,其他任何线程都无法再获得同一个monitor对象。 其实本质上没有区别,只是方法的同步是一种隐式的方式来实现,无需通过字节码来完成。

volatile

并发编程的3个基本概念:原子性、可见性和有序性。
volatile作用:

  1. 保证并发可见性(但不保证原子性),使工作内存中的变量强制刷新到主内存,使其他线程工作内存中的缓存失效。也就是一个线程对volatile变量的修改,能立即被其他线程感知到。
  2. 禁止jvm指令重排序优化,通过内存屏障实现,执行到volatile变量时,其前面的所有语句都执行完,后面所有语句都未执行。这点可以保证有序性。典型例子,单例模式DCL

由Java内存模型来直接保证的原子性变量操作包括read、load、assign、use、store和write,我们大致可以认为基本数据类型的访问读写是具备原子性的(long和double的非原子性协定例外,但商用虚拟机几乎都选择把64位数据的读写操作作为原子操作来对待,因此我们在编写代码时一般不需要把用到的long和double变量专门声明为volatile)
参考:https://blog.csdn.net/u012723673/article/details/80682208

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值