线程状态

解读
1.start() ≠ run()
/**
* 比较start()与run()的不同
*/
public class testThread {
public static class DemoThread extends Thread{
@Override
public void run() {
System.out.println("我是"+Thread.currentThread().getName()+"线程");//打印当前调用的线程
}
}
public static void main(String[] args){
Thread t1 = new DemoThread();//创建一个线程对象
t1.run();//调用run()方法
t1.start();//调用start()方法
}
}
Thread是一个线程类,但同时记住,它也是一个普通的类,很多人一开始能记住这个类的start()和run()是不一样的,但是随着学习的深入,这一点似乎已经越来越被淡忘了。
2.start()源码
public synchronized void start() {
//线程状态,0表示该线程是一个新建的状态,非0则会抛出异常
if (threadStatus != 0)
throw new IllegalThreadStateException();
//将这个线程对象添加到线程组进行管理,我们在这里可以理解为就是一个管理线程的容器,group如果我们在初始化的时候指定了其相应的线程组的对象,则会添加到我们所设置的,如果没有,则在哪个线程进行对象.start()的,就添加到相应的线程组里面或者是安全管理器的线程组。整个的线程启动是通过线程组管理的
group.add(this);
boolean started = false;//是否启动
try {
start0();//native方法,启动
started = true;//已经启动
} finally {
try {
if (!started) {//未启动
group.threadStartFailed(this);//启动失败
}
} catch (Throwable ignore) {
}
}
}
通过解读的1.2点,我们可以看到,在start()方法里面,并没有涉及到run()方法,我估计可能在start0()这个native方法中有所涉及,当然了,我也是估计下。
关于线程组的初始化,我们用下列代码来演示下
/**
* 比较start()与run()的不同
*/
public class testThread {
public static class DemoThread extends Thread{
@Override
public void run() {
System.out.println("我是"+Thread.currentThread().getName()+"线程");//打印当前调用的线程
System.out.println("我是"+Thread.currentThread().getThreadGroup()+"组");//打印当前调用的线程
}
}
public static void main(String[] args){
Thread t1 = new DemoThread();//创建一个线程对象
t1.run();//调用run()方法
t1.start();//调用start()方法
}
}
结果:
我是main线程
我是java.lang.ThreadGroup[name=main,maxpri=10]组
我是Thread-0线程
我是java.lang.ThreadGroup[name=main,maxpri=10]组
凭借这两点,我们可以看到解读1.2两点所写的基本正确。
3.初始化方法
所有的Thread类的构造方法最后都调用了一个名为init的方法
//ThreadGroup g 线程组
//Runnable target 调用run()
//String name 线程名字
//long stackSize 新线程所需的堆栈大小,或者零表示此参数将被忽略。简单来说,分配的资源的大小,内存的大小
//AccessControlContext acc 安全管理
//boolean inheritThreadLocals 变量的共享,在后续的笔记中会写到
private void init(ThreadGroup g, Runnable target, String name,
long stackSize, AccessControlContext acc,
boolean inheritThreadLocals) {
//name不能为空,在构造方法中,如果我们没有传递name参数,会自动给我们生成
if (name == null) {
throw new NullPointerException("name cannot be null");
}
this.name = name;
//当前调用的线程,例如,在main方法中去开启新线程,则会获得main线程
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. */
//parent线程组设置为当前线程的线程组
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();
//设置线程组,可手动亦可根据parent或者安全管理来设置,上面已经处理好了是怎么设置的。
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 */
//设置线程ID
tid = nextThreadID();
}
在源码中
//parent线程组设置为当前线程的线程组
if (g == null) {
g = parent.getThreadGroup();
}
可以解释我们在演示中为什么会出现同一个线程组的问题。
4.优先级
在init的源码中可以看到设置了一个优先级(同parent的优先级)
this.priority = parent.getPriority();
priority是优先级,可以理解为线程的先后顺序,值为1~10(越大分配的时间片越高),缺省为5,不过一般我们都不会去设置这个,因为有的操作系统并没有优先级这个概念,所以也不能保证到底那个能先运行。所以工作时,我们尽量少使用这个优先级。
设置优先级的代码:
public final void setPriority(int newPriority) {
ThreadGroup g;
checkAccess();
//设置的优先级不合理
if (newPriority > MAX_PRIORITY || newPriority < MIN_PRIORITY) {
throw new IllegalArgumentException();
}
//暂定,不是很理解
if((g = getThreadGroup()) != null) {
if (newPriority > g.getMaxPriority()) {
newPriority = g.getMaxPriority();
}
setPriority0(priority = newPriority);
}
}
状态图的方法
过时的:
- stop():强制线程停止执行,资源可能得不到正确的释放
- resumer():挂起恢复的线程,容易死锁
- suspend():挂起该线程,不容易释放资源
推荐:
- interrupt():中断一个线程,并不是强行关闭,中断标志位为true
- isInterrupted:判断当前线程是否中断
- interrupted:判断是否中断,中断标志位false
- true是中断,false是未中断
这个比较难以理解,我们做个代码演示下:
public class testThread {
public static class DemoThread extends Thread{
@Override
public void run(){
while (true){
System.out.println("线程名:"+Thread.currentThread().getName());
}
}
}
public static void main(String[] args) throws InterruptedException{
DemoThread t1 = new DemoThread();//创建一个线程对象
t1.start();
Thread.sleep(20);
t1.interrupt();
}
}
结果:不停的打印 线程名:Thread-0
看清楚我们在上面所说的,interrupt并不是强制关闭,所以这更像是一个协作式,在main里面interrupt了,但是在相应的线程里面,我们要处理这个中断标志位的改变,而这里并没有进行处理或者去有用到这个中断标志位。
我们再看下面的演示
public class testThread {
public static class DemoThread extends Thread{
@Override
public void run(){
while (!isInterrupted()){//有用到这个中断标志位哦
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("线程名:"+Thread.currentThread().getName());
}
}
}
public static void main(String[] args) throws InterruptedException{
DemoThread t1 = new DemoThread();//创建一个线程对象
t1.start();
Thread.sleep(20);
t1.interrupt();
}
}
结果:
java.lang.InterruptedException: sleep interrupted
at java.lang.Thread.sleep(Native Method)
at testThread$DemoThread.run(testThread.java:7)线程名:Thread-0
线程名:Thread-0
线程名:Thread-0
线程名:Thread-0
线程名:Thread-0
线程名:Thread-0
线程名:Thread-0
线程名:Thread-0
线程名:Thread-0
线程名:Thread-0
线程名:Thread-0
线程名:Thread-0
线程名:Thread-0
线程名:Thread-0
线程名:Thread-0
线程名:Thread-0
线程名:Thread-0
线程名:Thread-0
线程名:Thread-0
线程名:Thread-0
线程名:Thread-0
线程名:Thread-0
线程名:Thread-0
线程名:Thread-0
......
但是如果你仔细去分析这个代码,你会发现,为什么这个中断标志位改变了以后,仍然会不停的打印呢?
这是因为当抛出了异常以后,中断标志位会被自动复位位false,所以我们该如何进行修改呢?
很简单,在catch里面加上一句**interrupt();**就可以了。
public class testThread {
public static class DemoThread extends Thread{
@Override
public void run(){
while (!isInterrupted()){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
interrupt();
e.printStackTrace();
}
System.out.println("线程名:"+Thread.currentThread().getName());
}
}
}
public static void main(String[] args) throws InterruptedException{
DemoThread t1 = new DemoThread();//创建一个线程对象
t1.start();
Thread.sleep(20);
t1.interrupt();
}
}
结果:
java.lang.InterruptedException: sleep interrupted
at java.lang.Thread.sleep(Native Method)
at testThread$DemoThread.run(testThread.java:7)
线程名:Thread-0
在思考的时候,可以简单的理解为设置一个boolean类型的变量,去设置这个变量来协作式的操作线程的状态,但是要注意异常会复位这一点,另外,如果你深入的推敲这个代码,你会发现只要用到这个这个中断标志位的时候,就会抛出中断异常InterruptedException,所以,当我们看见一个方法抛出这个异常的时候,也就遇到了在底层时,这个方法会去对中断标志位进行一个处理。
yield():释放资源,并可以再次获得
sleep():释放资源,不可再次获得
再次:指的是下一次
本文详细解析了Java线程的内部机制,包括start()与run()的区别,线程状态,优先级设定,以及中断机制的正确使用。通过代码示例,深入浅出地讲解了线程的生命周期和管理。
1930

被折叠的 条评论
为什么被折叠?



