第一篇博文,说说为什么要写。
今天面了一个人,感觉他的知识不成体系,全靠零零碎碎的杂糅在一起。
所以,我要总结一下所学,构建知识网络。
1、生命周期
新建:new thread();
就绪:调用thread的start()方法,此为可运行状态。
running:当前线程抢到cpu执行权,正在运行。
blocked:堵塞状态,放弃当前cpu执行权。
dead:结束。
先分析一下:线程是哪些情况会进入blocked状态,哪些情况到dead状态。
1)blocked的几种情况:调用wait,被其他线程join,或者cpu调用导致放弃当前cpu执行权。 此时是 running->blocked,blocked结束后,线程回到runnnable状态。 runnable ->running -> blocked->runnable
2)dead:正常死亡或者程序直接挂掉。
2、创建并启动一个线程
先看看官方文档怎么说的。
There are two ways to create a new thread of execution. One is to declare a class to be a subclass of Thread. This subclass should override the run method of class Thread. An instance of the subclass can then be allocated and started.
The other way to create a thread is to declare a class that implements the Runnable interface. That class then implements the run method. An instance of the class can then be allocated, passed as an argument when creating Thread
第一个方式就是继承thread重写run方法,第二个方式,通过实现runnable接口,通过传参方式创建一个线程。
1、new Thread().start(); //继承方式
2、new Thread(()->{}).start(); //runnable方式
分析一下两种方式:
1)我们创建一个线程后,实际执行单元都是run方法,那么为什么不能直接调用run方法启动一个线程。
看下源码:
public synchronized void start() {
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) {
}
}
}
start方法内部会执行一个native的start0方法,而start0方法会调用run方法。
start0就回去启动一个线程。
所以直接调用run,仅仅是执行一个方法并不会创建一个线程。
关于设计思路:
start方法,我们可以认为它是一个模版方法,同时thread又实现runnable。将具体执行单元(run)抽象出一个接口。 通过这种方式,既可以子类重写,也可以接口实现并注入。 接口方式,实际上一种由开发者自定义策略的方式。
2)new thread().start(); 都没有进行子类重写run。会启动一个线程吗
会。只是没有执行单元而已。 下面是thread中run方法。
public void run() {
if (target != null) {
target.run();
}
}
3、从构造函数分析线程创建的过程
看两个构造函数吧,昨天手臂懂了手术。将就一下自己的身体。
public Thread(Runnable target) {
init(null, target, "Thread-" + nextThreadNum(), 0);
}
public Thread(ThreadGroup group, Runnable target) {
init(group, target, "Thread-" + nextThreadNum(), 0);
}
private void init(ThreadGroup g, Runnable target, String name,
long stackSize) {
init(g, target, name, stackSize, null, true);
}
1)线程名问题:
在不传入线程名时,默认采用,”thread-“+0++方式。
2)在定义一个线程时,我们实际上需要定义
线程的treadgroup,执行单元,名字,栈深(有些jvm设了有用,有些设了没用)
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();
}
/* If the security doesn't have a strong opinion of the matter
use the parent thread group. */
if (g == null) {
g = parent.getThreadGroup();
}
}
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 (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();
}
1)如果没定义thread group,它回去找它爹的thread group。在main中,就是找main的threadgroup。main就是它爹。
2) stacksize不设就设0就用jvm默认的。threadgroup和 stacksize单独开一张
4、线程id、优先级、以及守护线程
thread提供了,线程id的get。priority、deamon的get、set的api。
id的设置在thread初始化完成。init方法中,tid = nextThreadID(); 0++;
而priority、deamon的默认值呢:
this.daemon = parent.isDaemon();
this.priority = parent.getPriority();
init方法中,和它爹的保持一致。当然我们通过set更改。
其实在官方文档中也有说明。意思就是哪个线程创建了这个线程,他们的优先级和deamon就同
Every thread has a priority. Threads with higher priority are executed in preference to threads with lower priority. Each thread may or may not also be marked as a daemon. When code running in some thread creates a new Thread object, the new thread has its priority initially set equal to the priority of the creating thread, and is a daemon thread if and only if the creating thread is a daemon
详细说一下守护线程:
线程分为两种:
用户线程,守护线程。
举个例子解释下这两种的区别:
骑士护送公主去打怪升级,骑士负责守住打怪区域,公主负责打怪。公主挂了,骑士跟着挂。
上面例子可能不当,看官网。
Marks this thread as either a daemon thread or a user thread. The Java Virtual Machine exits when the only threads running are all daemon threads.
This method must be invoked before the thread is started.
什么意思呢:就是当jvm上面全是deamon的时候,jvm退出。
所以总结一下:
1、jvm中的所有用户进程死亡,deamon跟着死亡,jvm退出
2、setdeamon必须在thread.start之前。
3、然后补充一个,用过线程池的都知道。线程池会提供一个默认的threadfactory。
那么线程池创建的线程时怎么样的呢。
默认的会改变,线程的deamon(false)和优先级(normal)。
以后线程池的时候来说。
第一篇博客,同时技术不行。如果有看的,有疑惑,一起交流,3q。