走向JAVA(一)

JAVA 并发编程

Executor 源码分析

Future体系源码分析

创建线程的几种方式:
1 继承thread类
2 实现runnable接口,传参给new thread,创建线程。
3 实现callbale接口,传参给FutureTask包装,传给new thread 创建线程
4 Executor.execute(一个runnable实例对象),本质通过线程工厂在线程池中创建。
题外话,现在正式介绍Future体系
UML图,FutureTask实现了RunnableFuture接口,RunnableFuture继承了
Future和Runnable接口(接口可以继承接口,但是只有类能实现接口)
在这里插入图片描述
FutureTask保存了7种状态NEW, INTERRUPTING, COMPLETING, NORMAL,EXCEPTIONAL,CANCELLED,INTERRUPTED分别对应了6个数值。可比较的

//这是一个内部的callable对象,我们通过构造函数传入的callable对象将会保存在这里
//当任务执行完成后,callable会被置为null
private Callable<V> callable;
//保存任务执行的结果或者是get()方法抛出的异常,通过state来实现同步的
private Object outcome; 
//执行callable任务的线程,它是CAS操作。
private volatile Thread runner;
//等待线程的Treiber栈,Treiber是一种算法,Treiber栈是一种无阻塞栈。
private volatile WaitNode waiters;

public FutureTask(Callable<V> callable) { //可以看到构造方法可以传callable,或者runnable返回值
        if (callable == null)
            throw new NullPointerException();
        this.callable = callable;
        this.state = NEW;       // ensure visibility of callable
}

public FutureTask(Runnable runnable, V result) {
        this.callable = Executors.callable(runnable, result);
        this.state = NEW;       // ensure visibility of callable
}

下面的代码很重要:

// Unsafe mechanics
    private static final sun.misc.Unsafe UNSAFE;
    private static final long stateOffset;      //任务状态的偏移量
    private static final long runnerOffset;   //runner线程的偏移量   
    private static final long waitersOffset;  //Treiber栈的偏移量   
  //有了这些偏移量后,UNSAFE就能得到他们对应的值了
    static {
        try {
            UNSAFE = sun.misc.Unsafe.getUnsafe();
            Class<?> k = FutureTask.class;
            stateOffset = UNSAFE.objectFieldOffset
                (k.getDeclaredField("state"));
            runnerOffset = UNSAFE.objectFieldOffset
                (k.getDeclaredField("runner"));
            waitersOffset = UNSAFE.objectFieldOffset
                (k.getDeclaredField("waiters"));
        } catch (Exception e) {
            throw new Error(e);
        }
    }

//任务起动就调这个方法。配合***CAS***算法看
public void run() {
        //compareAndSwapObject方法有四个参建:第一个是某个对象,第二个是相对偏移量,有了这个偏移量 
        //就可以知道,内存块对应变量X的值了,第三个是:预期值,第四个是:更新值,如果X == 预期值,那 
        //么就将X的值更新为更新值,并返回true,或者返回fasle.「是一个CAS操作」
        
        //如果state == NEW,则runner = Thread.currentThread() ,返回true,取反之后,进入后面的计算。

        //如果state不是NEW的情况,说明任务已经被执行了,直接返回  
       //避免任务重新执行
        if (state != NEW ||
            !UNSAFE.compareAndSwapObject(this, runnerOffset,
                                         null, Thread.currentThread()))
            return; //一个或运算表明,这两个操作有个一为True,都直接返回,因为状态不为NEW,已经被执行了
        try {
            Callable<V> c = callable;
            if (c != null && state == NEW) {
                V result;
                //这一块的代码,建议大家反复读多几次
                //这一个ran变量用得真的是精彩,他主要的为了:他主要是不捕捉set()方法的异常
                //如果这里我们直接在  result = c.call();后面直接调set();那么最终的done()方法很可能出现异常
                //就会导致 setException()调用了,从而生命周期变成了
                //NEW -> COMPLETING -> NORMAL-> EXCEPTIONAL
                boolean ran;
                try {
                    result = c.call();//如果任务执行的时间比较少,那么在这里就体现出来了
                    ran = true;
                } catch (Throwable ex) {
                    result = null;
                    ran = false;
                    setException(ex);
                }
                if (ran)
                    set(result);
            }
        } finally {
         
            runner = null;
            //在任务执行的过程中,可能会调用cancel()
            //这里主要是不想让中断操作逃逸到run()方法之外
            int s = state;
            if (s >= INTERRUPTING) //s为interrupt状态
                handlePossibleCancellationInterrupt(s);
        }
}

//更改当前任务的状态并把任务执行的结果写入到outcome当中,最后由get()取出来用
protected void set(V v) {
        //将当前任务状态置为:COMPLETING
        if (UNSAFE.compareAndSwapInt(this, stateOffset, NEW, COMPLETING)) {
            outcome = v;
            //将当前任务状态置为:NORMAL
            UNSAFE.putOrderedInt(this, stateOffset, NORMAL); // final state
            finishCompletion();
        }
}

//完成任务后的收尾操作
private void finishCompletion() {
        // assert state > COMPLETING;
        for (WaitNode q; (q = waiters) != null;) {
             //  将Treiber栈的栈顶置为null,
            if (UNSAFE.compareAndSwapObject(this, waitersOffset, q, null)) {
                //遍历Treiber栈并唤醒所有节点的线程
                for (;;) {
                    Thread t = q.thread;
                    if (t != null) {
                        q.thread = null;//把当前节点置为无效节点
                        LockSupport.unpark(t);//这里唤醒的是awaitDone()阻塞的线程
                    }
                    WaitNode next = q.next;
                    if (next == null)
                        break;
                    q.next = null; // unlink to help gc
                    q = next;
                }
                break;
            }
        }
        //一个钩子方法,本类中,它是一个空实现,在子类中可以重写它。
        done();
        //最后把callable置为null.
        callable = null;        // to reduce footprint
    }
//修改当前任务的状态
protected void setException(Throwable t) {
        //将当前任务状态置为:COMPLETING
        if (UNSAFE.compareAndSwapInt(this, stateOffset, NEW, COMPLETING)) {
            outcome = t;
            //将当前任务状态置为:EXCEPTIONAL
            UNSAFE.putOrderedInt(this, stateOffset, EXCEPTIONAL); // final state
            finishCompletion();
        }
}

//取消任务。如果是false  NEW->CANCELLED     true: NEW ->INTERRUPTING->INTERRUPTED
public boolean cancel(boolean mayInterruptIfRunning) {
         //如果当前任务是新建任务,则将其置为INTERRUPTING或者CANCELLED
        if (!(state == NEW &&
              UNSAFE.compareAndSwapInt(this, stateOffset, NEW,
                  mayInterruptIfRunning ? INTERRUPTING : CANCELLED)))
            return false;
        try {    // in case call to interrupt throws exception
            if (mayInterruptIfRunning) {
                try {
                    //使用了线程中断的方法来达到取消任务的目的
                    Thread t = runner;
                    if (t != null)
                        t.interrupt();
                } finally { // final state
                    //如果当前任务不是新建任务,则将其状态置为INTERRUPTING
                    UNSAFE.putOrderedInt(this, stateOffset, INTERRUPTED);
                }
            }
        } finally {
            finishCompletion();
        }
        return true;

CAS算法

CAS 操作包含三在这里插入代码片个操作数 —— 内存位置(V)、预期原值(A)和新值(B)。 如果内存位置的值与预期原值相匹配,那么处理器会自动将该位置值更新为新值 。否则,处理器不做任何操作。无论哪种情况,它都会在 CAS 指令之前返回该 位置的值。(在 CAS 的一些特殊情况下将仅返回 CAS 是否成功,而不提取当前 值。)CAS 有效地说明了“我认为位置 V 应该包含值 A;如果包含该值,则将 B 放到这个位置;否则,不要更改该位置,只告诉我这个位置现在的值即可。”
Unsafe类的compareAndSwapInt()方法为例来说,compareAndSwapInt就是借助C语言和汇编代码来实现的。

public final native boolean compareAndSwapInt(Object o,
											  long offset,
                                              int expected,
                                              int x);

x86汇编指令cmpxchg本身保证了原子性,其实就是cpu的CAS操作的实现,那么问题来了,为什么保证了原子性还需要在多核处理器中加上lock前缀呢?

答案是:多核处理器中不能保证可见性,lock前缀可以保证这行汇编中所有值的可见性,这个问题的原因是多核CPU中缓存导致的(x86中罪魁祸首是store buffer的存在)。

这样通过lock前缀保障多核处理器的可见性,然后通过cmpxchg指令完成CPU上原子性的CAS操作,完美解决问题!

volatile关键字

一旦一个共享变量(类的成员变量、类的静态成员变量)被volatile修饰之后,那么就具备了两层语义:
  1)保证了不同线程对这个变量进行操作时的可见性,即一个线程修改了某个变量的值,这新值对其他线程来说是立即可见的。
  2)禁止进行指令重排序。
volatile 作用
1)它确保指令重排序时不会把其后面的指令排到内存屏障之前的位置,也不会把前面的指令排到内存屏障的后面;即在执行到内存屏障这句指令时,在它前面的操作已经全部完成;

2)它会强制将对缓存的修改操作立即写入主存;
Java内存模型规定所有的变量都是存在主存()当中(类似于前面说的物理内存),每个线程都有自己的工作内存()(类似于前面的高速缓存)。线程对变量的所有操作都必须在工作内存中进行,而不能直接对主存进行操作。并且每个线程不能访问其他线程的工作内存。

3)如果是写操作,它会导致其他CPU中对应的缓存行无效。

参考博文:
https://blog.csdn.net/cringkong/article/details/80533917
https://www.cnblogs.com/dolphin0520/p/3920373.html

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值