JDK源码 -- Thread/Runnable、Callable/FutureTask与ThreadLocal

版权声明:转载原创博客请附上原文链接 https://blog.csdn.net/weixin_43495590/article/details/88813122

一:Thread与Runnable

Thread类实现Runnable接口,接下来主要围绕Thread展开讨论

1.1 主要属性
	//使用Thrad-加上这个编号组成默认的线程名字
    private static int threadInitNumber; 
    private volatile char  name[]; //名字

	//生成的线程ID,tid等于这个属性
    private static long threadSeqNumber;
    private long tid; // ID
    
    private int priority; //优先级
    private boolean daemon = false; //守护线程标记
    private Runnable target; // 初始化注入,run()会判断该属性
    
    //线程分组,线程分组以后可以进行一些批量的线程操作
    private ThreadGroup group; 
    //线程私有变量,后面详细讲解
    ThreadLocal.ThreadLocalMap threadLocals = null; 
    //线程状态标志,调用start()方法的时候会进行判断,0表示线程尚未启动
    private volatile int threadStatus = 0; 
    
    //三个优先级常亮,分别是1、5、10表示线程优先级最小是1,最大是10
    public final static int MIN_PRIORITY = 1;
    public final static int NORM_PRIORITY = 5;
    public final static int MAX_PRIORITY = 10;
1.2 构造函数
    private void init(ThreadGroup g, Runnable target, String name,
                      long stackSize, AccessControlContext acc) {
         
         // 初始化未传入名字采用"Thread-"+threadInitNumber构成
        if (name == null) {
            throw new NullPointerException("name cannot be null");
        }
        this.name = name.toCharArray();
		// 子线程构造函数中的currentThread指代父线程
        Thread parent = currentThread();
        SecurityManager security = System.getSecurityManager(); 
        if (g == null) {
            if (security != null) {
                g = security.getThreadGroup(); 
            }
            if (g == null) {
                g = parent.getThreadGroup();
            }
        }
        g.checkAccess();
        if (security != null) {
            if (isCCLOverridden(getClass())) {
                security.checkPermission(SUBCLASS_IMPLEMENTATION_PERMISSION);
            }
        }
        g.addUnstarted();
        this.group = g;
       
       	// 父子线程优先级、守护标记默认保持一致
        this.daemon = parent.isDaemon(); 
        this.priority = parent.getPriority(); 
        if (security == null || isCCLOverridden(parent.getClass()))
            this.contextClassLoader = parent.getContextClassLoader();
        else
            this.contextClassLoader = parent.contextClassLoader;
        this.inheritedAccessControlContext =
                acc != null ? acc : AccessController.getContext();
        this.target = target;
        setPriority(priority);
        if (parent.inheritableThreadLocals != null)
            this.inheritableThreadLocals =
                ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);
        this.stackSize = stackSize;
        // 赋值线程ID
        tid = nextThreadID();
    }

通过构造函数都会调用的init()方法可得到以下结论:

  1. 线程名字在初始化未传入的时候默认使用threadInityNumber属性构建
  2. 子线程构造函数中currentThread指代父线程需要明确
  3. 子线程的守护标志、优先级与父线程保持一致
  4. 针对上一个结论补充一点显示指定的线程优先级最大值一定是线程组优先级与自定义优先级中的最小值
1.3 主要方法
 public static native void yield();
 
 public static native void sleep(long millis) throws InterruptedException;

yield:表示当前线程愿意放弃CPU资源,但是请求可能被忽略,该方法具备不确定性

sleep:使线程进入TIME_WAITING状态,线程不会释放资源

    public void interrupt() {
        if (this != Thread.currentThread())
            checkAccess();
        synchronized (blockerLock) {
            Interruptible b = blocker;
            if (b != null) {
                interrupt0();// Just to set the interrupt flag
                b.interrupt(this);
                return;
            }
        }
        interrupt0();
    }

interrupt:线程加上中断标记,但凡带有中断标记的线程处于等待阻塞状态都抛出InterruptedException异常

	public static boolean interrupted() {
	        return currentThread().isInterrupted(true);
	}
	
	public boolean isInterrupted() {
	        return isInterrupted(false);
	}
	
	private native boolean isInterrupted(boolean ClearInterrupted);
    Thread thread = Thread.currentThread();
    boolean interrupted = thread.isInterrupted();
    boolean interrupted1 = thread.isInterrupted();
    System.out.println(interrupted1); //输出结果false

interrupted:检测中断标记不清除,内部调用isInterrupted()
isInterrupted:检测标记并清除

  public final synchronized void join(long millis)
    throws InterruptedException {
        long base = System.currentTimeMillis();
        long now = 0;

        if (millis < 0) {
            throw new IllegalArgumentException("timeout value is negative");
        }
		// 参数0表示等待至线程执行完毕
		// 参数不为0表示串行线程最大执行时间
        if (millis == 0) {
            while (isAlive()) { 
                wait(0); 
            }
        } else {
            while (isAlive()) {
                long delay = millis - now;
                if (delay <= 0) { 
                    break;
                }
                wait(delay); 
                now = System.currentTimeMillis() - base;
            }
        }
    }

join:线程合并即串行执行。A线程中如果调用B线程join(),A线程执行wait()陷入等待。注意以下两点:

  1. join()生效必须在start()启动线程后执行
  2. join(0)与wait(0)类似,都表示无限时长
1.4 线程状态

Thread内部枚举类State定义线程状态对象

public enum State {
        NEW,RUNNABLE,BLOCKED,
        WAITING, TIMED_WAITING,TERMINATED;
}
  • NEW:新建,线程类通过new关键字刚实例化时的状态
  • RUNNABLE:可执行,线程调用start()后等待CPU资源的状态
  • BLOCKED:阻塞,等待线程锁
  • WAITING:阻塞,等待特定方法执行,如notify()
  • TIMED_WAITING:阻塞,如sleep()后等待时间到达
  • TERMINATED:终止、销毁

二:Callable与FutureTask

@FunctionalInterface
public interface Callable<V> {
    V call() throws Exception;
}

Callable函数式接口,抽象方法call()。线程类实现接口重写call(),配合FutureTask使用。相对于Thraed/Runnable有如下两个特点:

  • 可以获取返回值
  • 可以抛出异常
2.1 主要属性
	// FutureTask对象状态
	// 创建、完成、正常、异常、取消、打断中、打断完成
    private volatile int state;
    private static final int NEW          = 0;
    private static final int COMPLETING   = 1;
    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;

	// 构造函数注入,run()调用执行的线程类
    private Callable<V> callable;
	private volatile Thread runner;
	
   	// 储存get()所需返回值数据
    private Object outcome;
   	
   	// get()阻塞使用,内部类记录阻塞线程节点
    private volatile WaitNode waiters;
2.2 构造函数
    public FutureTask(Callable<V> callable) {
        if (callable == null)
            throw new NullPointerException();
        this.callable = callable;
        this.state = NEW;       
    }
    public FutureTask(Runnable runnable, V result) {
        this.callable = Executors.callable(runnable, result);
        this.state = NEW;       
    }

构造函数完成两项操作,注意第二个构造函数第二个参数表示显示指定get()方法返回值

  1. 注入run()方法调用线程类对象
  2. 初始化FutureTask对象状态为NEW
2.3 主要方法 — 线程启动
    public void run() {
        if (state != NEW ||
            !UNSAFE.compareAndSwapObject(this, runnerOffset,
                                         null, Thread.currentThread()))
            return;
        try {
        	// 判断构造为Callable亦或是Runnable
            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);
                }
                // call()执行完毕储存返回值
                if (ran)
                    set(result);
            }
        } finally {
            runner = null;
            int s = state;
            if (s >= INTERRUPTING)
                handlePossibleCancellationInterrupt(s);
        }
    }

FutureTask实现RunnableFuture接口,该接口继承Runnable、Future接口提供唯一抽象方法run(),call()的执行在run()中完成

2.4 主要方法 – 返回值获取
    public V get() throws InterruptedException, ExecutionException {
        int s = state;
        // 状态判断,小于等于完成状态陷入阻塞等待awaitDone()
        // awaitDone()方法中for(;;)自旋操作等待线程执行完毕
        if (s <= COMPLETING)
            s = awaitDone(false, 0L);
        return report(s);
    }
    private V report(int s) throws ExecutionException {
        Object x = outcome;
        // FutureTask状态正常返回返回值
        if (s == NORMAL)
            return (V)x;
        // 状态为被打断亦或是取消则抛出异常 
        if (s >= CANCELLED)
            throw new CancellationException();
        throw new ExecutionException((Throwable)x);
    }
        ThreadClassTest tct = new ThreadClassTest();
        FutureTask<String> futureTask = new FutureTask<>(tct,"futuretask返回值测试");
        //futureTask.run();
        String s1 = futureTask.get(5000, TimeUnit.MILLISECONDS);
        System.out.println(s1);

使用get()方法一定要记住这是一个阻塞方法,如果未调用run()执行修改状态很可能会陷入自旋操作无限等待。当然get()重载实现可以限制等待时间长度与单位

三:ThreadLocal

线程私有变量,关心点莫过于如何实现数据在线程中的私有,接下来着重辨析数据储存、获取的过程解读私有

3.1 内部类ThreadLocalMap
	// 初始化数组大小
    private static final int INITIAL_CAPACITY = 16;
	
	// 储存数据Entry数组
    private Entry[] table;
	
	// 数组使用内存
    private int size = 0;

根据内部类ThreadLocalMap主要属性看出数据储存使用Entry数组,这个Entry类如下所示:

    static class Entry extends WeakReference<ThreadLocal<?>> {
        Object value;
		Entry(ThreadLocal<?> k, Object v) {
            super(k);
            value = v;
        }
    }

Entry类继承弱引用WeakReference,就一个value属性

3.2 线程私有
    public T get() {
    	// 获取当前线程
        Thread t = Thread.currentThread();
        // 获取线程属性ThreadLocalMap实例
        ThreadLocalMap map = getMap(t);
        if (map != null) {
        	// ThreadLocal对象为key获取Entry对象
            ThreadLocalMap.Entry e = map.getEntry(this);
            if (e != null) {
                @SuppressWarnings("unchecked")
                T result = (T)e.value;
                return result;
            }
        }
        return setInitialValue();
    }
    
    ThreadLocalMap getMap(Thread t) {
        return t.threadLocals;
    }
    
    private Entry getEntry(ThreadLocal<?> key) {
    	// 计算ThreadLocal对象下标,获取对应Entry对象
        int i = key.threadLocalHashCode & (table.length - 1);
        Entry e = table[i];
        if (e != null && e.get() == key)
            return e;
        else
            return getEntryAfterMiss(key, i, e);
    }
  1. 每个线程储存数据的ThreadLocalMap互相独立,保证线程私有
  2. ThreadLocalMap储存数据使用Entry[]数组,根据ThreadLocal对象哈希值离散计算数组下标,保证线程可以储存多个私有变量

猜你喜欢

转载自blog.csdn.net/weixin_43495590/article/details/88813122