线程的生命周期
线程的生命周期一共有5种状态:new、runnable、running、blocked、dead.
新建(new Thread)
实例化一个Thread类的对象,此线程即进入new状态,分配有自己的内存空间,但该线程并没有运行,此时线程not alive。
就绪(runnable)
线程已经被启动,在等待被分配给CPU时间片。也就通过调用线程实例的start()方法来启动线程,进入runnbale。runnable状态的线程已经具备了运行条件,但还没有被分到CPU,不一定会被立即执行,线程处于就绪队列中。此时线程alive。
运行(running)
线程获得CPU资源正在执行任务(run()方法),除非此线程自动放弃CPU资源或者有优先级更高的线程进入,线程将一直运行到结束。线程alive。
堵塞(blocked)
由于某种原因导致正在运行的线程让出CPU并暂停自己的执行,即进入阻塞状态。
- 正在睡眠:用sleep(long t)方法。一个睡眠的进程在指定的时间过去可进入就绪状态。
- 正在等待:调用wait()。可用notify()回到就绪状态。
- 被另一个线程所阻塞:调用suspend()方法。可调用resume()回复。
此时线程alive。
死亡(dead)
线程执行完毕或被其他线程杀死。此时线程不可能再进入就绪状态等待执行。
- 自然终止:正常运行run()方法后终止。
- 异常终止:调用stop()方法让一个线程终止运行
处于Dead状态的线程not alive。
守护线程
运行在后台的线程。如Java的垃圾回收,数据库连接池等都是守护线程。
守护线程与普通线程在写法上没有太大区别,调用线程对象的方法setDaemon(true)就可以将其设置为守护线程。
public final void setDaemon(boolean on) //该方法碧玺在启动线程前调用
当正在运行的线程都是守护线程时,Java虚拟机退出。即,Java虚拟的运行只关系非守护线程,当非守护线程都运行完毕时,无论守护线程是否退出,Java虚拟机都会停止。
当前线程副本:ThreadLocal
ThreadLocal为每个使用该变量的线程提供独立的变量副本,每一个线程都可以独立地改变自己的副本,而不会影响其他线程所对应的副本。
实现原理
ThreadLocal的set()方法源码:
/**
* Sets the current thread's copy of this thread-local variable
* to the specified value. Most subclasses will have no need to
* override this method, relying solely on the {@link #initialValue}
* method to set the values of thread-locals.
*
* @param value the value to be stored in the current thread's copy of
* this thread-local.
*/
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
分析它的实现:首先通过getMap(Thread t)方法获取一个和当前线程相关的ThreadLocalMap,然后将变量的值设置到这个ThreadLocalMap对象中,若map == null,则新建一个ThreadLocalMap然后再赋值。
线程隔离的秘密
ThreadLocalMap是ThreadLocal类的一个静态内部类,实现了键值对的设置和获取。每个线程中都有一个独立的ThreadLocalMap副本,它所存储的值,只能被当前线程读取和修改。 ThreadLocal类通过操作每一个线程特有的ThreadLocalMap副本,从而实现了变量访问在不同线程中的隔离。 ThreadLocalMap存储的键值对中的键是this对象指向的ThreadLocal对象。