线程类一共有八种构造方法
public Thread() {
init(null, null, "Thread-" + nextThreadNum(), 0);
}
public Thread(Runnable target) {
init(null, target, "Thread-" + nextThreadNum(), 0);
}
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);
}
/**
* 分配一个新的Thread对象,并传入一个target作为其运行对象,将指定线程名name,
* 以及所属线程组group ,并具有指定的堆栈大小stackSize 。
* 堆栈大小是虚拟机为该线程的堆栈分配的大致的地址空间字节数。
* stackSize参数的(如果有的话)与平台有关。
*线程栈的大小,这个值一般是CPU页面的整数倍。如x86的页面大小是4KB。在x86平台下,默认的线程栈大小是 12KB。
* 在某些平台上,指定了一个较高的值stackSize参数可以允许抛出一个前一个线程来实现更大的递归深
* 度StackOverflowError 。 类似地,指定较低的值可能允许更多数量的线程同时存在,而不会抛出
* OutOfMemoryError (或其他内部错误)。 所述stackSize参数的值和最大递归深度和并发水平之间
* 的关系的细节是依赖于平台的。 在某些平台上,该值stackSize参数可能没有任何效果。
*
* stackSize参数只是被当作一种建议大小传入虚拟机。 如果平台的指定值不合理地低,虚拟机可能会改为
* 使用一些平台特定的最小值; 如果指定的值不合理地高,虚拟机可能会使用一些平台特定的最大值。
* 同样,虚拟机可以自由地按照合适的方式向上或向下舍入指定的值(或完全忽略它)。
*
* 由于此构造函数的行为有依赖于平台的性质,因此在使用时应特别小心。 执行给定计算所需的线程栈大小
* 可能会因JRE实现而异。 鉴于这种变化,可能需要仔细调整堆栈大小参数,并且可能需要对要运行应用程序
* 的每个JRE实现重复调整。
*/
public Thread(ThreadGroup group, Runnable target, String name,
long stackSize) {
init(group, target, name, stackSize);
}
当采用 new方式创建一个线程对象时,线程构造器会调用一个**init()**方法来进行初始化。
/**
* Initializes a Thread with the current AccessControlContext.
* 使用当前的AccessControlContext初始化线程,八种构造方法都会先进入到这里
*/
private void init(ThreadGroup g, Runnable target, String name,
long stackSize) {
init(g, target, name, stackSize, null, true);
}
/**
* 初始化一个线程
* @param g 线程组
* @param target 调用run()方法的对象
* @param name 线程名
* @param stackSize 新线程的所需堆栈大小,0表示该参数将被忽略。
* @param acc 要继承的AccessControlContext;如果为null,则为 AccessController.getContext()
* @param inheritThreadLocals 如果为true,则从构造线程继承可继承线程局部变量的初始值
*/
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) {
/* 如果有安全管理者, 先尝试获取安全管理者的线程组*/
if (security != null) {
g = security.getThreadGroup();
}
/* 如果此时线程组g还是null会采用当前线程的线程组。*/
if (g == null) {
g = parent.getThreadGroup();
}
}
//对线程组g进行检查
//主要检查:1.是否线程组为null 2.是否根线程组,如果是根线程组则要检查其是否有修改权限
g.checkAccess();
//权限检查:子类不能覆盖对安全性敏感的方法,否则将检查“enableContextClassLoaderOverride”运行时权限
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;//为Runnable实例赋值
setPriority(priority);//设置线程优先级
//设置inheritableThreadLocals属性
if (inheritThreadLocals && parent.inheritableThreadLocals != null)
this.inheritableThreadLocals =
ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);
this.stackSize = stackSize;//设置栈大小
tid = nextThreadID();//设置线程ID(此方法已加锁)
}
启动线程
/**
*调用此方法,这个线程开始执行,jvm虚拟机会调用run()方法
* 一个线程被启动多次是不合法的
* 特别是,线程一旦完成执行就可能不会重新启动。
*/
public synchronized void start() {
/**
*VM创建/设置的主方法线程或“系统”组线程不会调用此方法。将来可能会向该方法添加任何添加到此方法的新功能。
* 零状态值对应于状态“ NEW”
*/
if (threadStatus != 0)
throw new IllegalThreadStateException();
/* 通知组该线程将要启动,以便可以将其添加到组的线程列表中,并且可以减少该组的未启动计数。 */
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
不做什么。如果start0抛出了Throwable,则它将被向上传递到调用堆栈*/
}
}
}
private native void start0();//本地方法,真正启动线程的方法
总结:让线程对象调用start()方法和run()方法的区别。
线程是一条按照顺序执行的任务流程。无论是单线程还是多线程,每条线程中的任务都必须按顺序完成。
如果现在我们希望有些任务能够同时执行,那必须把它们分别放在不同的线程中。
想要实现多线程同时执行的效果,必须要有一个行为能够开启新的线程。
run()方法本身没有特殊功能。如果直接调用run()方法,并没有开启新的线程。就和我们之前调用一个add()、setName()、getAge()、sayHello()等等方法是同样的效果。只是在main这一条线程中,进入到了另外一个方法的执行。那么就必须要等这个方法中所有的代码全部执行完毕,才会返回到调用处继续执行后边的代码。
如果调用start()方法,会间接调用start()中一个名为strat0()的native方法,在start0()的底层实现过程中,才会真正开启另外一条线程。这条线程会调用我们线程对象的run()方法。start()方法可以理解为是一种短暂性、瞬时性的方法。只需要将线程开启,把它丢到一旁去自己运行,而start()就算是执行结束了,并不需要等这条线程结束。