一: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()方法可得到以下结论:
- 线程名字在初始化未传入的时候默认使用
threadInityNumber
属性构建 - 子线程构造函数中
currentThread指代父线程
需要明确 - 子线程的
守护标志、优先级
与父线程保持一致 - 针对上一个结论补充一点显示指定的线程优先级
最大值
一定是线程组优先级与自定义优先级中的最小值
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()陷入等待。注意以下两点:
- join()生效必须在start()
启动线程后执行
- 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()方法返回值
注入run()方法调用线程类对象
初始化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);
}
- 每个线程储存数据的
ThreadLocalMap互相独立
,保证线程私有 - ThreadLocalMap储存数据使用Entry[]数组,根据ThreadLocal对象
哈希值离散计算数组下标
,保证线程可以储存多个私有变量