继上两篇博文,学习,分享了虚拟机的内存自动管理机制,分别包括了java运行时数据区域和HotSpot虚拟机中对象的内存分配、布局、以及访问定位,今天穿插着学习一下java多线程,首先以java.lang.Thread类入手,学习研究Thread类的源码。
线程就是程序中的一个执行任务。java虚拟机允许一个应用同时执行多个线程,此处引出java多线程实现原理:多个线程轮流切换并分配处理器执行时间。
每个线程都拥有一个优先级(为什么?线程调度的方式有2种,分时调度与抢占调度;分时调度就是所有线程轮流获取CPU使用权,每个线程平均分配CPU执行时间(时间分片);抢占调度就是优先让优先级高的线程占用CPU,并且优先级高的线程占用CPU的时间相对低优先级的线程多,如果线程的优先级相同,便随机选择一个线程占用CPU,java中线程的调度方式为抢占调度)。优先级高的线程优先执行。每个线程都有可能被标记为“daemon”守护进程。当代码执行的时候,某些线程创建了一个新的Thread对象,新创建的线程在其初始化的过程中会将其优先级设置为等同于其父线程的优先级,当且仅当其父线程为daemon时,它才为daemon。关于守护进程,有人说JVM的垃圾收集器(Garbage Collection)就是一个守护进程。后续我会用2到3篇的博文来学习分享JVM的垃圾收集器。
当java虚拟机启动后,通常会伴随着一个非守护进程的线程(某些指定类的main()方法),java虚拟机会一直执行线程直至下列情况发生:
①Runtime类的exit()方法被调用并且安全管理器允许这个行为发生。
②所有非守护进程的线程都已经死掉,要么通过run()方法返回,要么抛出一个异常
创建线程的方式有两种。一是声明一个Thread的子类(说白了就是集成Thread),并重写run()方法。然后这个子类分配一个实例并启动。例如:
* <hr><blockquote><pre>
* class PrimeThread extends Thread {
* long minPrime;
* PrimeThread(long minPrime) {
* this.minPrime = minPrime;
* }
*
* public void run() {
* // compute primes larger than minPrime
* . . .
* }
* }
* </pre></blockquote><hr>
下面代码,创建一个线程并启动它。
* <blockquote><pre>
* PrimeThread p = new PrimeThread(143);
* p.start();
* </pre></blockquote>
另一种方式就是声明一个类实现Runnable接口,并实现其run()方法,然后这个类会分配一个实例,并作为Tread构造器的一个参数,创建并start。例如:
* <hr><blockquote><pre>
* class PrimeRun implements Runnable {
* long minPrime;
* PrimeRun(long minPrime) {
* this.minPrime = minPrime;
* }
*
* public void run() {
* // compute primes larger than minPrime
* . . .
* }
* }
* </pre></blockquote><hr>
* <blockquote><pre>
* PrimeRun p = new PrimeRun(143);
* new Thread(p).start();
* </pre></blockquote>
为了识别,每个线程都有一个name。多个thread可能会有相同的名字。
一、线程的状态
从java程序的角度,既在Thread类的源码中,定义了6种线程的状态,分别为:NEW、RUNNABLE、BLOCKED、WAITING、TIMED_WAITING、TERMINATED。
/**
* A thread state. A thread can be in one of the following states:
* <ul>
* <li>{@link #NEW}<br>
* A thread that has not yet started is in this state.
* </li>
* <li>{@link #RUNNABLE}<br>
* A thread executing in the Java virtual machine is in this state.
* </li>
* <li>{@link #BLOCKED}<br>
* A thread that is blocked waiting for a monitor lock
* is in this state.
* </li>
* <li>{@link #WAITING}<br>
* A thread that is waiting indefinitely for another thread to
* perform a particular action is in this state.
* </li>
* <li>{@link #TIMED_WAITING}<br>
* A thread that is waiting for another thread to perform an action
* for up to a specified waiting time is in this state.
* </li>
* <li>{@link #TERMINATED}<br>
* A thread that has exited is in this state.
* </li>
* </ul>
*
* <p>
* A thread can be in only one state at a given point in time.
* These states are virtual machine states which do not reflect
* any operating system thread states.
*
* @since 1.5
* @see #getState
*/
public enum State {
/**
* Thread state for a thread which has not yet started.线程已创建,但还没有start();
*/
NEW,
/**
* Thread state for a runnable thread. A thread in the runnable
* state is executing in the Java virtual machine but it may
* be waiting for other resources from the operating system
* such as processor.此状态的线程正在java虚拟机中执行但也有可能等待一些操作系统资源
*/
RUNNABLE,
/**
* Thread state for a thread blocked waiting for a monitor lock.
* A thread in the blocked state is waiting for a monitor lock
* to enter a synchronized block/method or
* reenter a synchronized block/method after calling
* {@link Object#wait() Object.wait}.状态blocked就是线程等待一个同步块或同步方法释放锁或可重入的同步块或方法中调用wait()
*/
BLOCKED,
/**
* Thread state for a waiting thread.等待状态
* A thread is in the waiting state due to calling one of the
* following methods:等待状态是由于调用了下列的方法
* <ul>
* <li>{@link Object#wait() Object.wait} with no timeout</li>
* <li>{@link #join() Thread.join} with no timeout</li>其实Thread.join()底层方法与Object.wait()都是调用了JNI
* <li>{@link LockSupport#park() LockSupport.park}</li>
* </ul>
*
* <p>A thread in the waiting state is waiting for another thread to
* perform a particular action.
*
* For example, a thread that has called <tt>Object.wait()</tt>
* on an object is waiting for another thread to call
* <tt>Object.notify()</tt> or <tt>Object.notifyAll()</tt> on
* that object. A thread that has called <tt>Thread.join()</tt>
* is waiting for a specified thread to terminate.
*/
WAITING,
/**
* Thread state for a waiting thread with a specified waiting time.
* A thread is in the timed waiting state due to calling one of
* the following methods with a specified positive waiting time:有时间限制的等待状态
* <ul>
* <li>{@link #sleep Thread.sleep}</li>
* <li>{@link Object#wait(long) Object.wait} with timeout</li>
* <li>{@link #join(long) Thread.join} with timeout</li>
* <li>{@link LockSupport#parkNanos LockSupport.parkNanos}</li>
* <li>{@link LockSupport#parkUntil LockSupport.parkUntil}</li>
* </ul>
*/
TIMED_WAITING,
/**
* Thread state for a terminated thread.结束状态
* The thread has completed execution.线程完成执行
*/
TERMINATED;
}
二、Thread类构造器
public Thread() {
init(null, null, "Thread-" + nextThreadNum(), 0);
}
public Thread(Runnable target) {
init(null, target, "Thread-" + nextThreadNum(), 0);
}
Thread(Runnable target, AccessControlContext acc) {
init(null, target, "Thread-" + nextThreadNum(), 0, acc, false);
}
public Thread(ThreadGroup group, Runnable target) {
init(group, target, "Thread-" + nextThreadNum(), 0);
}
public Thread(String name) {
init(null, null, name, 0);
}
public Thread(ThreadGroup group, String name) {
init(group, null, name, 0);
}
public Thread(Runnable target, String name) {
init(null, target, name, 0);
}
public Thread(ThreadGroup group, Runnable target, String name) {
init(group, target, name, 0);
}
public Thread(ThreadGroup group, Runnable target, String name,
long stackSize) {
init(group, target, name, stackSize);
}
线程所有构造器内部都调用了init方法,init方法如下:
/**
* Initializes a Thread with the current AccessControlContext.
* @see #init(ThreadGroup,Runnable,String,long,AccessControlContext,boolean)
*/
private void init(ThreadGroup g, Runnable target, String name,
long stackSize) {
init(g, target, name, stackSize, null, true);
}
init方法内部又调用了init(ThreadGroup,Runnable,String,long,AccessControlContext,boolean)方法:
/**
* Initializes a Thread.
*
* @param g the Thread group
* @param target the object whose run() method gets called
* @param name the name of the new Thread
* @param stackSize the desired stack size for the new thread, or
* zero to indicate that this parameter is to be ignored.栈大小:新线程所需栈大小,如果为0表示忽略此参数
* @param acc the AccessControlContext to inherit, or
* AccessController.getContext() if null
* @param inheritThreadLocals if {@code true}, inherit initial values for
* inheritable thread-locals from the constructing thread
*/
private void init(ThreadGroup g, Runnable target, String name,
long stackSize, AccessControlContext acc,
boolean inheritThreadLocals) {
if (name == null) {//线程名称不能为空
throw new NullPointerException("name cannot be null");
}
//命名新创建的线程
this.name = name;
//获取父线程(即当前正在创建新线程的线程)
Thread parent = currentThread();
SecurityManager security = System.getSecurityManager();
if (g == null) {
/* Determine if it's an applet or not */
/* If there is a security manager, ask the security manager
what to do. 从安全管理器中获取线程组*/
if (security != null) {
g = security.getThreadGroup();
}
/* If the security doesn't have a strong opinion of the matter
use the parent thread group. 如果安全管理器未获取到线程组,则在父线程中获取*/
if (g == null) {
g = parent.getThreadGroup();
}
}
/* checkAccess regardless of whether or not threadgroup is
explicitly passed in. 检查线程组能否访问*/
g.checkAccess();
/*
* Do we have the required permissions?
*/
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);
*****************************
//改变线程优先级:源码实现
public final void setPriority(int newPriority) {
ThreadGroup g;
checkAccess();//首先确认当前执行的线程是否拥有修改这个线程的许可(SecurityManager security = System.getSecurityManager();如果安全管理器存在,调用其checkAccess(Thread arg)方法)
//然后该线程的优先级被设置为newPriority,该优先级允许的最大值为其线程组的优先级。
if (newPriority > MAX_PRIORITY || newPriority < MIN_PRIORITY) {
throw new IllegalArgumentException();
}
if((g = getThreadGroup()) != null) {
if (newPriority > g.getMaxPriority()) {
newPriority = g.getMaxPriority();
}
setPriority0(priority = newPriority);
}
}
****************************
if (inheritThreadLocals && parent.inheritableThreadLocals != null)
this.inheritableThreadLocals =
ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);
/* Stash the specified stack size in case the VM cares */
this.stackSize = stackSize;
/* Set thread ID */
tid = nextThreadID();
}
三、Thread类常用方法
线程礼让
/**
* A hint to the scheduler that the current thread is willing to yield
* its current use of a processor. The scheduler is free to ignore this
* hint.
* 对调度器的一个暗示,当前线程愿意让出它处理器的使用权。调度器可以自由的忽略这个暗示
* <p> Yield is a heuristic attempt to improve relative progression
* between threads that would otherwise over-utilise a CPU. Its use
* should be combined with detailed profiling and benchmarking to
* ensure that it actually has the desired effect.
* Yield
* <p> It is rarely appropriate to use this method. It may be useful
* for debugging or testing purposes, where it may help to reproduce
* bugs due to race conditions. It may also be useful when designing
* concurrency control constructs such as the ones in the
* {@link java.util.concurrent.locks} package.
*/
public static native void yield();
线程启动
/**
* Causes this thread to begin execution; the Java Virtual Machine
* calls the <code>run</code> method of this thread.
*致使线程开始执行;java虚拟机调用这个线程的run()方法
* <p>
* The result is that two threads are running concurrently: the
* current thread (which returns from the call to the
* <code>start</code> method) and the other thread (which executes its
* <code>run</code> method).
* <p>
* 这里会有2个线程同时去执行,当前线程去执行start()方法,另一个线程去执行run()方法
* It is never legal to start a thread more than once.
* In particular, a thread may not be restarted once it has completed
* execution.
*
* @exception IllegalThreadStateException if the thread was already
* started.
* @see #run()
* @see #stop()
*/
public synchronized void start() {
/**
* This method is not invoked for the main method thread or "system"
* group threads created/set up by the VM. Any new functionality added
* to this method in the future may have to also be added to the VM.
*
* A zero status value corresponds to state "NEW".
*/
if (threadStatus != 0)
throw new IllegalThreadStateException();
/* Notify the group that this thread is about to be started
* so that it can be added to the group's list of threads
* and the group's unstarted count can be decremented. */
group.add(this);
boolean started = false;
try {
start0();
started = true;
} finally {
try {
if (!started) {
group.threadStartFailed(this);
}
} catch (Throwable ignore) {
/* do nothing. If start0 threw a Throwable then
it will be passed up the call stack */
}
}
}
private native void start0();
/**
* If this thread was constructed using a separate
* <code>Runnable</code> run object, then that
* <code>Runnable</code> object's <code>run</code> method is called;
* otherwise, this method does nothing and returns.
* <p>
* Subclasses of <code>Thread</code> should override this method.
*
* @see #start()
* @see #stop()
* @see #Thread(ThreadGroup, Runnable, String)
*/
@Override
public void run() {
if (target != null) {
target.run();
}
}